<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>sunwengang blog</title>
  
  <subtitle>wizzie</subtitle>
  <link href="/Blog/atom.xml" rel="self"/>
  
  <link href="https://alonealive.github.io/Blog/"/>
  <updated>2021-06-10T14:30:03.752Z</updated>
  <id>https://alonealive.github.io/Blog/</id>
  
  <author>
    <name>sunwengang</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Android Display/Graphics自绘UML图[置顶]</title>
    <link href="https://alonealive.github.io/Blog/2059/01/01/2021/590101_android_display_graphics/"/>
    <id>https://alonealive.github.io/Blog/2059/01/01/2021/590101_android_display_graphics/</id>
    <published>2059-01-01T03:11:11.000Z</published>
    <updated>2021-06-10T14:30:03.752Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Android Display/Graphics相关模块绘制的自绘UML图整合（部分流程图参考非最新AOSP）</p></blockquote><a id="more"></a><h2 id="绘制序列图"><a href="#绘制序列图" class="headerlink" title="绘制序列图"></a>绘制序列图</h2><blockquote><p>文章：<a href="https://wizzie.top/Blog/2020/07/07/2020/200707_android_HWUI_Draw/#%E7%BB%98%E5%88%B6%E5%BA%8F%E5%88%97%E5%9B%BE" target="_blank" rel="noopener">Android HWUI绘制流程-绘制序列图</a></p></blockquote><p><img src="%E7%A1%AC%E4%BB%B6%E6%B8%B2%E6%9F%93%E9%A1%BA%E5%BA%8F%E5%9B%BE.png" alt="Android HWUI窗口绘制"></p><hr><h2 id="Android-图形显示框架"><a href="#Android-图形显示框架" class="headerlink" title="Android 图形显示框架"></a>Android 图形显示框架</h2><h3 id="显示框架概述"><a href="#显示框架概述" class="headerlink" title="显示框架概述"></a>显示框架概述</h3><blockquote><p>文章：<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/#%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6%E6%A6%82%E8%BF%B0" target="_blank" rel="noopener">Android 图形显示框架-显示框架概述）</a></p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-0-%E6%A6%82%E8%BF%B0.png" alt="显示框架概述"></p><hr><h3 id="窗口移除序列图"><a href="#窗口移除序列图" class="headerlink" title="窗口移除序列图"></a>窗口移除序列图</h3><blockquote><p>文章：<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/#%E7%AA%97%E5%8F%A3%E7%A7%BB%E9%99%A4%E5%BA%8F%E5%88%97%E5%9B%BE%EF%BC%88Activity-destroy%EF%BC%89" target="_blank" rel="noopener">Android 图形显示框架-窗口移除序列图（Activity destroy）</a></p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-1-3-Activity_Destroy.png" alt="Activity destroy"></p><hr><h3 id="Surface-Destroy（Activity-pause或者stop状态）"><a href="#Surface-Destroy（Activity-pause或者stop状态）" class="headerlink" title="Surface Destroy（Activity pause或者stop状态）"></a>Surface Destroy（Activity pause或者stop状态）</h3><blockquote><p>文章：<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/#Surface-Destroy%EF%BC%88Activity-pause%E6%88%96%E8%80%85stop%E7%8A%B6%E6%80%81%EF%BC%89" target="_blank" rel="noopener">Android 图形显示框架-Surface Destroy（Activity pause或者stop状态）</a></p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-1-2-Surface_Destroy.png" alt="Surface Destroy"></p><hr><h3 id="surface创建序列图"><a href="#surface创建序列图" class="headerlink" title="surface创建序列图"></a>surface创建序列图</h3><blockquote><p>文章：<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/#%E5%BA%8F%E5%88%97%E5%9B%BE" target="_blank" rel="noopener">Android 图形显示框架-surface创建序列图</a></p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-1-1-Surface%E5%88%9B%E5%BB%BA.png" alt="Surface创建"></p><hr><h3 id="BufferQueue序列图"><a href="#BufferQueue序列图" class="headerlink" title="BufferQueue序列图"></a>BufferQueue序列图</h3><h4 id="dequeuebuffer-amp-queuebuffer"><a href="#dequeuebuffer-amp-queuebuffer" class="headerlink" title="dequeuebuffer &amp; queuebuffer"></a>dequeuebuffer &amp; queuebuffer</h4><blockquote><p>文章：<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/#%E5%BA%8F%E5%88%97%E5%9B%BE" target="_blank" rel="noopener">Android 图形显示框架-dequeuebuffer &amp; queuebuffer</a></p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-2-%E6%B8%B2%E6%9F%93%E6%97%B6BufferQueue-dequeue-queue.png" alt="渲染时BufferQueue的dequeue和queue操作"></p><hr><h4 id="acquire-amp-release"><a href="#acquire-amp-release" class="headerlink" title="acquire &amp; release"></a>acquire &amp; release</h4><blockquote><p>文章：<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/#acquire-amp-release" target="_blank" rel="noopener">Android 图形显示框架-acquire &amp; release</a></p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-3-BufferQueue-acquire-release.png" alt="合成时BufferQueue的acquire和release"></p><hr><h2 id="SurfaceFlinger进程启动"><a href="#SurfaceFlinger进程启动" class="headerlink" title="SurfaceFlinger进程启动"></a>SurfaceFlinger进程启动</h2><p><img src="1-SF%E9%A1%BA%E5%BA%8F%E5%9B%BE.png" alt="SurfaceFlinger进程启动"></p><hr><p><img src="SF%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B.png" alt="SurfaceFlinger启动"></p><hr><h2 id="SurfaceFlinger工作流程"><a href="#SurfaceFlinger工作流程" class="headerlink" title="SurfaceFlinger工作流程"></a>SurfaceFlinger工作流程</h2><p><img src="SF%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.png" alt="SurfaceFlinger工作流程"></p><hr><h2 id="SurfaceFlinger-onFrameAvailble类关系图"><a href="#SurfaceFlinger-onFrameAvailble类关系图" class="headerlink" title="SurfaceFlinger onFrameAvailble类关系图"></a>SurfaceFlinger onFrameAvailble类关系图</h2><p><img src="SF-%E6%B6%88%E8%B4%B9%E8%80%85onFrameAvailable.png" alt="SurfaceFlinger onFrameAvailble"></p><hr><h2 id="Angle流程图"><a href="#Angle流程图" class="headerlink" title="Angle流程图"></a>Angle流程图</h2><blockquote><p>Angle是一款专为Android平台设计的，敏捷且适合快速开发的2D游戏引擎，基于OpenGL ES技术开发。具体流程后续文章整理。</p></blockquote><p><img src="ANGLE_investigation.png" alt="Angle流程图"></p><hr>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Android Display/Graphics相关模块绘制的自绘UML图整合（部分流程图参考非最新AOSP）&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>C++11/14/17/2特性</title>
    <link href="https://alonealive.github.io/Blog/2021/06/16/2021/210616_cpp_cpp11_1/"/>
    <id>https://alonealive.github.io/Blog/2021/06/16/2021/210616_cpp_cpp11_1/</id>
    <published>2021-06-16T13:42:00.000Z</published>
    <updated>2021-06-16T07:42:05.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>C++11/14/17/20部分新的特性语法</p></blockquote><a id="more"></a><h2 id="C-11被弃用的主要特性"><a href="#C-11被弃用的主要特性" class="headerlink" title="C++11被弃用的主要特性"></a>C++11被弃用的主要特性</h2><h3 id="char修饰符"><a href="#char修饰符" class="headerlink" title="char修饰符"></a>char修饰符</h3><p>不再允许字符串字面值常量赋值给一个<code>char *</code>。如果需要用字符串字面值常量赋值和初始化一个<code>char *</code>,应该使用<code>const char *</code> 或者<code>auto</code></p><p><code>char *str = &quot;hello world!&quot;; // 将出现弃用警告</code></p><ul><li><ul><li>auto，不指定变量类型，编译器将把变量的类型设置成和初始值相同</li></ul></li><li><ul><li>const，限定符</li></ul></li></ul><p>PS：另一个限定符Volatile关键词，易变的。所谓的易变性，在汇编层面反映出来，就是两条语句，下一条语句不会直接使用上一条语句对应的volatile变量的寄存器内容，而是重新从内存中读取。</p><hr><h3 id="智能指针之unique-ptr"><a href="#智能指针之unique-ptr" class="headerlink" title="智能指针之unique_ptr"></a>智能指针之unique_ptr</h3><p>auto_ptr被弃用,应使用<code>unique_ptr</code></p><p>C++11提供了3种智能指针类型，它们分别由unique_ptr类、shared_ptr类和weak_ptr 类定义，所以又分别称它们为独占指针、共享指针和弱指针（均定义在头文件<code>#include &lt;memory&gt;</code>）</p><p>智能指针是一个可以像指针一样工作的对象，但是当它不再被使用时，可以自动删除动态分配的内存。智能指针背后的核心概念是动态分配内存的所有权。</p><p>智能指针背后的核心概念是动态分配内存的所有权。智能指针被称为可以拥有或管理它所指向的对象。当需要让单个指针拥有动态分配的对象时，可以使用独占指针。对象的所有权可以从一个独占指针转移到另一个指针，其转移方式为：对象始终只能有一个指针作为其所有者。<strong>当独占指针离开其作用域或将要拥有不同的对象时，它会自动释放自己所管理的对象。</strong></p><p>共享指针将记录有多少个指针共同享有某个对象的所有权。<strong>当有更多指针被设置为指向该对象时，引用计数随之增加；当指针和对象分离时，则引用计数也相应减少。当引用计数降低至0时，该对象被删除。</strong></p><p>智能指针实际上是一个对象，在对象的外面包围了一个拥有该对象的普通指针。这个包围的常规指针称为裸指针。</p><h4 id="move-函数"><a href="#move-函数" class="headerlink" title="move()函数"></a>move()函数</h4><p>C++ 提供了一个move()库函数，可用于将对象的所有权从一个独占指针转移到另外一个独占指针：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="built_in">unique_ptr</span>&lt;<span class="keyword">int</span>&gt; <span class="title">uptr1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>)</span></span>; <span class="comment">//定义并初始化</span></span><br><span class="line">*uptr1 = <span class="number">15</span>;  <span class="comment">//赋值</span></span><br><span class="line"><span class="built_in">unique_ptr</span>&lt;<span class="keyword">int</span>&gt; uptr3; <span class="comment">// 正确</span></span><br><span class="line">uptr3 = move (uptr1) ; <span class="comment">// 将所有权从 uptr1 转移到 uptr3</span></span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; *uptr3 &lt;&lt; <span class="built_in">endl</span>; <span class="comment">// 打印 15</span></span><br></pre></td></tr></table></figure><h4 id="函数传参"><a href="#函数传参" class="headerlink" title="函数传参"></a>函数传参</h4><p>不能直接通过值给函数传递一个智能指针，因为通过值传递将导致复制真正的形参。如果要让函数通过值接收一个独占指针，则在调用函数时，必须对真正的形参使用move()函数：</p><p>结果将打印来自于函数fun()中的10</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//函数使用通过值传递的形参</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">fun</span><span class="params">(<span class="built_in">unique_ptr</span>&lt;<span class="keyword">int</span>&gt; uptrParam)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; *uptrParam &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">unique_ptr</span>&lt;<span class="keyword">int</span>&gt; <span class="title">uptr</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>)</span></span>;</span><br><span class="line">    *uptr = <span class="number">10</span>;</span><br><span class="line">    fun (move (uptr)); <span class="comment">// 在调用中使用 move</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果通过引用传递的方式，那就不必对真正的形参使用move()函数了。示例代码如下：</p><p>结果将打印数字15：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//函数使用通过引用传递的值</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">fun</span><span class="params">(<span class="built_in">unique_ptr</span>&lt;<span class="keyword">int</span>&gt;&amp; uptrParam)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; *uptrParam &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">unique_ptr</span>&lt;<span class="keyword">int</span>&gt; <span class="title">uptr</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>)</span></span>;</span><br><span class="line">    *uptr1 = <span class="number">15</span>;</span><br><span class="line">    fun (uptr1) ; <span class="comment">//在调用中无须使用move</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="make-unique-函数创建对象"><a href="#make-unique-函数创建对象" class="headerlink" title="make_unique()函数创建对象"></a>make_unique<T>()函数创建对象</h4><p>从C++14开始，有一个库函数<code>make_unique&lt;T&gt;()</code>可用于创建<code>unique_ptr</code>对象。该函数分配一个类型为T的对象，然后返回一个拥有该对象的独占指针。例如：</p><p><code>unique_ptr&lt;int&gt; uptr(new int);</code></p><p>现在可以替换成：</p><p><code>unique_ptr&lt;int&gt; uptr = make_unique&lt;int&gt;();</code></p><h4 id="unique-ptr-类的成员函数"><a href="#unique-ptr-类的成员函数" class="headerlink" title="unique_ptr 类的成员函数"></a>unique_ptr 类的成员函数</h4><table><thead><tr><th align="center">unique_ptr成员函数</th><th align="center">描 述</th></tr></thead><tbody><tr><td align="center">reset()</td><td align="center">销毁由该智能指针管理的任何可能存在的对象。该智能指针被置为空</td></tr><tr><td align="center">reset(T* ptr)</td><td align="center">销毁由该智能指针当前管理的任何可能存在的对象。该智能指针继续控制由裸指针ptr指向的对象</td></tr><tr><td align="center">get()</td><td align="center">返回该智能指针管理的由裸指针指向的对象。如果某个指针需要传递给函数，但是 该函数并不知道该如何操作智能指针，则get()函数非常有用</td></tr></tbody></table><hr><h3 id="register关键字弃用"><a href="#register关键字弃用" class="headerlink" title="register关键字弃用"></a>register关键字弃用</h3><p>register关键字被弃用,可以使用但不再具备任何实际含义</p><h3 id="bool类型的-操作弃用"><a href="#bool类型的-操作弃用" class="headerlink" title="bool类型的++操作弃用"></a>bool类型的++操作弃用</h3><p>bool类型的++操作被弃用</p><h3 id="noexcept"><a href="#noexcept" class="headerlink" title="noexcept"></a>noexcept</h3><p>C++98异常说明、unexpected_handler、set_unexpected() 等相关特性被弃用,应该使用<code>noexcept</code></p><p><strong>C++11新标准引入的noexcept运算符，可以用于指定某个函数不抛出异常</strong>。预先知道函数不会抛出异常有助于简化调用该函数的代码，而且编译器确认函数不会抛出异常，它就能执行某些特殊的优化操作。</p><figure class="highlight cpp"><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"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> x)</span> <span class="keyword">noexcept</span></span>;  <span class="comment">//不抛出异常</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">(<span class="keyword">int</span> x)</span></span>;  <span class="comment">//抛出异常</span></span><br></pre></td></tr></table></figure><p>noexcept可以接受一个可选的实参，该参数必须能转换为bool类型:</p><figure class="highlight cpp"><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"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> x)</span> <span class="title">noexcept</span><span class="params">(<span class="literal">true</span>)</span></span>;  <span class="comment">//不抛出异常</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> x)</span> <span class="title">noexcept</span><span class="params">(<span class="literal">false</span>)</span></span>; <span class="comment">//抛出异常</span></span><br></pre></td></tr></table></figure><hr><h3 id="C语言风格的类型转换被弃用"><a href="#C语言风格的类型转换被弃用" class="headerlink" title="C语言风格的类型转换被弃用"></a>C语言风格的类型转换被弃用</h3><p>即在变量前使用<code>(convert_type)</code>,应该使用<code>static_cast</code>、<code>reinterpret_cast</code>、<code>const_cast</code>、<code>dynamic_cast</code>来进行类型转换</p><p>为了使潜在风险更加细化，使问题追溯更加方便，使书写格式更加规范，C++对类型转换进行了分类，并新增了四个关键字来予以支持，它们分别是：</p><table><thead><tr><th align="center">关键字</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">static_cast</td><td align="center">用于良性转换，一般不会导致意外发生，风险很低</td></tr><tr><td align="center">const_cast</td><td align="center">用于const与非const、volatile与非volatile之间的转换</td></tr><tr><td align="center">reinterpret_cast</td><td align="center">高度危险的转换，这种转换仅仅是对二进制位的重新解释，不会借助已有的转换规则对数据进行调整，但是可以实现最灵活的C++类型转换</td></tr><tr><td align="center">dynamic_cast</td><td align="center">借助 RTTI，用于类型安全的向下转型(Downcasting)</td></tr></tbody></table><p><strong>这四个关键字的语法格式都是一样的：</strong></p><p><code>xxx_cast&lt;newType&gt;(data)</code></p><p>老式的C语言转换类型（已弃用）：</p><figure class="highlight c"><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">double</span> scores = <span class="number">95.5</span>;</span><br><span class="line"><span class="keyword">int</span> n = (<span class="keyword">int</span>)scores;</span><br></pre></td></tr></table></figure><p>C++新风格：</p><figure class="highlight cpp"><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">double</span> scores = <span class="number">95.5</span>;</span><br><span class="line"><span class="keyword">int</span> n = <span class="keyword">static_cast</span>&lt;<span class="keyword">int</span>&gt;(scores);</span><br></pre></td></tr></table></figure><h4 id="static-cast关键字"><a href="#static-cast关键字" class="headerlink" title="static_cast关键字"></a>static_cast关键字</h4><p><strong>static_cast 是“静态转换”的意思，也就是在编译期间转换，转换失败的话会抛出一个编译错误。</strong></p><p>static_cast（静态转换）只能用于良性转换，转换风险较低，例如：</p><ul><li>原有的自动类型转换，例如 short 转 int、int 转 double、const 转非 const、向上转型等；</li><li>void指针和具体类型指针之间的转换，例如<code>void *</code>转<code>int *</code>、<code>char *</code>转<code>void *</code>等；</li><li>有转换构造函数或者类型转换函数的类与其它类型之间的转换，例如<code>double</code>转<code>Complex</code>（调用转换构造函数）、<code>Complex</code>转<code>double</code>（调用类型转换函数）</li></ul><p>static_cast不能用于无关类型之间的转换，因为这些转换都是有风险的，例如：</p><ol><li>两个具体类型指针之间的转换，例如<code>int *</code>转<code>double *</code>、<code>Student *</code>转<code>int *</code>等。不同类型的数据存储格式不一样，长度也不一样，用A类型的指针指向B类型的数据后，会按照A类型的方式来处理数据：如果是读取操作，可能会得到一堆没有意义的值；如果是写入操作，可能会使 B 类型的数据遭到破坏，当再次以 B 类型的方式读取数据时会得到一堆没有意义的值。</li><li>int和指针之间的转换。将一个具体的地址赋值给指针变量是非常危险的，因为该地址上的内存可能没有分配，也可能没有读写权限，恰好是可用内存反而是小概率事件</li><li>static_cast也不能用来去掉表达式的const修饰和volatile修饰。换句话说，不能将const/volatile类型转换为非const/volatile类型</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">    <span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line">    <span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstdlib&gt;</span></span></span><br><span class="line">    <span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">Complex</span>&#123;</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        Complex(<span class="keyword">double</span> real = <span class="number">0.0</span>, <span class="keyword">double</span> imag = <span class="number">0.0</span>): m_real(real), m_imag(imag)&#123; &#125;</span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        <span class="function"><span class="keyword">operator</span> <span class="title">double</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123; <span class="keyword">return</span> m_real; &#125;  <span class="comment">//类型转换函数</span></span><br><span class="line">    <span class="keyword">private</span>:</span><br><span class="line">        <span class="keyword">double</span> m_real;</span><br><span class="line">        <span class="keyword">double</span> m_imag;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">//下面是正确的用法</span></span><br><span class="line">        <span class="keyword">int</span> m = <span class="number">100</span>;</span><br><span class="line">        <span class="function">Complex <span class="title">c</span><span class="params">(<span class="number">12.5</span>, <span class="number">23.8</span>)</span></span>;</span><br><span class="line">        <span class="keyword">long</span> n = <span class="keyword">static_cast</span>&lt;<span class="keyword">long</span>&gt;(m);  <span class="comment">//宽转换，没有信息丢失</span></span><br><span class="line">        <span class="keyword">char</span> ch = <span class="keyword">static_cast</span>&lt;<span class="keyword">char</span>&gt;(m);  <span class="comment">//窄转换，可能会丢失信息</span></span><br><span class="line">        <span class="keyword">int</span> *p1 = <span class="keyword">static_cast</span>&lt;<span class="keyword">int</span>*&gt;( <span class="built_in">malloc</span>(<span class="number">10</span> * <span class="keyword">sizeof</span>(<span class="keyword">int</span>)) );  <span class="comment">//将void指针转换为具体类型指针</span></span><br><span class="line">        <span class="keyword">void</span> *p2 = <span class="keyword">static_cast</span>&lt;<span class="keyword">void</span>*&gt;(p1);  <span class="comment">//将具体类型指针，转换为void指针</span></span><br><span class="line">        <span class="keyword">double</span> real= <span class="keyword">static_cast</span>&lt;<span class="keyword">double</span>&gt;(c);  <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="keyword">float</span> *p3 = <span class="keyword">static_cast</span>&lt;<span class="keyword">float</span>*&gt;(p1);  <span class="comment">//不能在两个具体类型的指针之间进行转换</span></span><br><span class="line">-        p3 = <span class="keyword">static_cast</span>&lt;<span class="keyword">float</span>*&gt;(<span class="number">0X2DF9</span>);  <span class="comment">//不能将整数转换为指针类型</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="const-cast关键字"><a href="#const-cast关键字" class="headerlink" title="const_cast关键字"></a>const_cast关键字</h4><blockquote><p>const_cast用来去掉表达式的const修饰或volatile修饰。换句话说，const_cast就是用来将const/volatile 类型转换为非const/volatile类型。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> n = <span class="number">100</span>;</span><br><span class="line">    <span class="comment">//&amp;n用来获取n的地址，它的类型为const int *，必须使用const_cast转换为int *类型后才能赋值给 p</span></span><br><span class="line">    <span class="comment">//由于p指向了n，并且n占用的是栈内存，有写入权限，所以可以通过p修改n的值</span></span><br><span class="line">    <span class="keyword">int</span> *p = <span class="keyword">const_cast</span>&lt;<span class="keyword">int</span>*&gt;(&amp;n);</span><br><span class="line">    *p = <span class="number">234</span>;</span><br><span class="line">    <span class="built_in">cout</span>&lt;&lt;<span class="string">"n = "</span>&lt;&lt;n&lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">    <span class="built_in">cout</span>&lt;&lt;<span class="string">"*p = "</span>&lt;&lt;*p&lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="reinterpret-cast关键字"><a href="#reinterpret-cast关键字" class="headerlink" title="reinterpret_cast关键字"></a>reinterpret_cast关键字</h4><blockquote><p>reinterpret_cast 这种转换仅仅是对二进制位的重新解释，不会借助已有的转换规则对数据进行调整，非常简单粗暴，所以风险很高</p></blockquote><p>不建议使用</p><h4 id="dynamic-cast关键字"><a href="#dynamic-cast关键字" class="headerlink" title="dynamic_cast关键字"></a>dynamic_cast关键字</h4><blockquote><p>dynamic_cast用于在类的继承层次之间进行类型转换，它既允许向上转型（Upcasting），也允许向下转型（Downcasting）。向上转型是无条件的，不会进行任何检测，所以都能成功；向下转型的前提必须是安全的，要借助RTTI进行检测，所有只有一部分能成功。</p></blockquote><p><font color=red>dynamic_cast与static_cast是相对的，dynamic_cast是“动态转换”的意思，static_cast是“静态转换”的意思。dynamic_cast会在程序运行期间借助 RTTI进行类型转换，这就要求基类必须包含虚函数；static_cast在编译期间完成类型转换，能够更加及时地发现错误。</font></p><p>dynamic_cast的语法格式为：</p><p><code>dynamic_cast &lt;newType&gt; (expression)</code></p><hr><h2 id="常量nullptr"><a href="#常量nullptr" class="headerlink" title="常量nullptr"></a>常量nullptr</h2><blockquote><p>nullptr出现的目的是为了替代NULL。在某种意义上来说,传统C++会把NULL、0视为同一种东西,这取决于编译器如何定义NULL,有些编译器会将NULL定义为<code>((void*)0)</code>,有些则会直接将其定义为0</p></blockquote><p>C++不允许直接将<code>void *</code>隐式转换到其他类型。但如果编译器尝试把<code>NULL</code>定义为<code>((void*)0)</code></p><p>例如：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">foo</span><span class="params">(<span class="keyword">char</span>*)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">foo</span><span class="params">(<span class="keyword">int</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//无法通过编译，将会去调用 foo(int),从而导致代码违反直觉</span></span><br><span class="line">foo(<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//调用foo(char*)</span></span><br><span class="line">foo(<span class="literal">nullptr</span>);</span><br></pre></td></tr></table></figure><p>为了解决这个问题，C++11引入了nullptr关键字,专门用来区分空指针、0。</p><p>而nullptr的类型为<code>nullptr_t</code>，能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。</p><p><font color=red>因此NULL不同于0与nullptr。所以,请养成直接使用nullptr的习惯</font></p><hr><h2 id="常量constexpr"><a href="#常量constexpr" class="headerlink" title="常量constexpr"></a>常量constexpr</h2><p>C++本身已经具备了常量表达式的概念,比如<code>1+2</code>,<code>3*4</code>这种表达式总是会产生相同的结果并且没有任何副作用。<strong>如果编译器能够在编译时就把这些表达式直接优化并植入到程序运行时,将能增加程序的性能</strong></p><p>C++11 提供了 constexpr 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式</p><p>**</p><h2 id="if-switch变量声明强化"><a href="#if-switch变量声明强化" class="headerlink" title="if/switch变量声明强化"></a>if/switch变量声明强化</h2><p>在传统C++中,变量的声明虽然能够位于任何位置,甚至于for语句内能够声明一个临时变量int,但始终没有办法在if和switch语句中声明一个临时的变量。</p><p>C++17消除了这一限制,使得我们可以在<code>if(或switch)</code>中完成这一操作：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;algorithm&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; vec = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//在c++17之前</span></span><br><span class="line"><span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::iterator itr = <span class="built_in">std</span>::find(vec.begin(), vec.end(), <span class="number">2</span>);</span><br><span class="line"><span class="keyword">if</span> (itr != vec.end()) &#123;</span><br><span class="line">*itr = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 需要重新定义一个新的变量</span></span><br><span class="line"><span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::iterator itr2 = <span class="built_in">std</span>::find(vec.begin(), vec.end(), <span class="number">3</span>);</span><br><span class="line"><span class="keyword">if</span> (itr2 != vec.end()) &#123;</span><br><span class="line">*itr2 = <span class="number">4</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">//C++17</span></span><br><span class="line"><span class="comment">// 将临时变量放到 if 语句内</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::iterator itr = <span class="built_in">std</span>::find(vec.begin(), vec.end(), <span class="number">3</span>);</span><br><span class="line">itr != vec.end()) &#123;</span><br><span class="line">*itr = <span class="number">4</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><hr><h2 id="decltype"><a href="#decltype" class="headerlink" title="decltype"></a>decltype</h2><p>decltype关键字是为了解决auto关键字只能对变量进行类型推导的缺陷而出现的。它的用法和typeof很相似:</p><p><code>decltype(表达式)</code></p><p>有时候,我们可能需要计算某个表达式的类型,例如:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">auto</span> x = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">auto</span> y = <span class="number">2</span>;</span><br><span class="line"><span class="keyword">decltype</span>(x+y) z;</span><br></pre></td></tr></table></figure><hr><h2 id="if-constexpr"><a href="#if-constexpr" class="headerlink" title="if constexpr"></a>if constexpr</h2><p>C++11引入了constexpr关键字,它将表达式或函数编译为常量结果。</p><p>如果我们把这一特性引入到条件判断中去,让代码在编译时就完成分支判断,岂不是能让程序效率更高?</p><p>C++17将constexpr这个关键字引入到if语句中,允许在代码中声明常量表达式的判断条件</p><p>代码示例：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="comment">//函数模板</span></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">auto</span> <span class="title">print_type_info</span><span class="params">(<span class="keyword">const</span> T&amp; t)</span> </span>&#123;</span><br><span class="line"><span class="function"><span class="keyword">if</span> <span class="title">constexpr</span> <span class="params">(<span class="built_in">std</span>::is_integral&lt;T&gt;::value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> t + <span class="number">1</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> t + <span class="number">0.001</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; print_type_info(<span class="number">5</span>) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; print_type_info(<span class="number">3.14</span>) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在编译时，实际代码会表现为：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">print_type_info</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span>&amp; t)</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> t + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">double</span> <span class="title">print_type_info</span><span class="params">(<span class="keyword">const</span> <span class="keyword">double</span>&amp; t)</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> t + <span class="number">0.001</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; print_type_info(<span class="number">5</span>) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; print_type_info(<span class="number">3.14</span>) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="区间for迭代"><a href="#区间for迭代" class="headerlink" title="区间for迭代"></a>区间for迭代</h2><p>C++11引入了基于范围的迭代写法,我们能够写出像Python一样简洁的循环语句：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; vec = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">    <span class="comment">//std::find可用于查找容器中是否存在某个特定值</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">auto</span> itr = <span class="built_in">std</span>::find(vec.begin(), vec.end(), <span class="number">3</span>); itr != vec.end()) *itr = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> element : vec)</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; element &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>; <span class="comment">// read only</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;element : vec) &#123;</span><br><span class="line">        element += <span class="number">1</span>;       <span class="comment">// writeable</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span> element : vec)</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; element &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>; <span class="comment">// read only</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="外部模板"><a href="#外部模板" class="headerlink" title="外部模板"></a>外部模板</h2><p>C++模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理,仅在运行时处理那些最核心的动态服务,进而大幅优化运行期的性能。</p><p>传统C++中,模板只有在使用时才会被编译器实例化。换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板的实例化。</p><p>为此,<strong>C++11引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使我们能够显式的通知编译器何时进行模板的实例化:</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> <span class="class"><span class="keyword">class</span> <span class="title">std</span>:</span>:<span class="built_in">vector</span>&lt;<span class="keyword">bool</span>&gt;;</span><br><span class="line"><span class="comment">// 强行实例化</span></span><br><span class="line"><span class="keyword">extern</span> <span class="keyword">template</span> <span class="class"><span class="keyword">class</span> <span class="title">std</span>:</span>:<span class="built_in">vector</span>&lt;<span class="keyword">double</span>&gt;; <span class="comment">// 不在该当前编译文件中实例化模板</span></span><br></pre></td></tr></table></figure><hr><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li>书籍：《现代C++教程：高速上手 C++11/14/17/20》</li><li><a href="http://c.biancheng.net/view/1478.html" target="_blank" rel="noopener">C++智能指针unique_ptr详解</a></li><li><a href="https://blog.csdn.net/shang_0122/article/details/102208507" target="_blank" rel="noopener">C++函数传参三种形式（包含形参与实参）</a></li><li><a href="https://www.jianshu.com/p/08a53d8c9670" target="_blank" rel="noopener">C++11：noexcept关键字</a></li><li><a href="http://c.biancheng.net/cpp/biancheng/view/3297.html" target="_blank" rel="noopener">C++四种类型转换运算符：static_cast、dynamic_cast、const_cast和reinterpret_cast</a></li><li><a href="https://www.cnblogs.com/god-of-death/p/7852394.html" target="_blank" rel="noopener">C/C++ Volatile关键词深度剖析</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;C++11/14/17/20部分新的特性语法&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/categories/cpp/"/>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/tags/cpp/"/>
    
  </entry>
  
  <entry>
    <title>Android Display/Graphics调试技巧（六月份更新）</title>
    <link href="https://alonealive.github.io/Blog/2021/06/07/2021/210607_android_debug3/"/>
    <id>https://alonealive.github.io/Blog/2021/06/07/2021/210607_android_debug3/</id>
    <published>2021-06-07T13:52:00.000Z</published>
    <updated>2021-06-08T14:50:41.463Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>android display/graphics相关的调试方法和优化工作效率技巧整理</p></blockquote><a id="more"></a><h2 id="RenderThread渲染绘制（结合systrace）"><a href="#RenderThread渲染绘制（结合systrace）" class="headerlink" title="RenderThread渲染绘制（结合systrace）"></a>RenderThread渲染绘制（结合systrace）</h2><blockquote><p>参考：<a href="https://zhuanlan.zhihu.com/p/145219630" target="_blank" rel="noopener">Android Systrace 基础知识(7) - Vsync 解读</a><br>参考：<a href="https://blog.csdn.net/hexiaolong2009/article/details/99225637" target="_blank" rel="noopener">BufferQueue 学习总结（内附动态图）</a><br>参考：<a href="https://blog.csdn.net/weixin_29587979/article/details/113043321" target="_blank" rel="noopener">android 判断list里有这条数据_Android图形渲染原理（中）</a></p></blockquote><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></pre></td><td class="code"><pre><span class="line">----</span><br><span class="line">frameworks&#x2F;base&#x2F;libs&#x2F;hwui&#x2F;renderthread   -- DrawFrameTask::drawFrame()   -----&gt;</span><br><span class="line">DrawFrameTask::postAndWait()   ----&gt;</span><br><span class="line">DrawFrameTask::run()    -----&gt;</span><br><span class="line">(1) DrawFrameTask::syncFrameState  同步Frame的State  ----&gt;</span><br><span class="line">CanvasContext::prepareTree</span><br><span class="line">(2) context-&gt;draw()   绘制    ------&gt;</span><br><span class="line">frameworks&#x2F;base&#x2F;libs&#x2F;hwui&#x2F;renderthread  -- CanvasContext::draw()   ----&gt;</span><br><span class="line">----</span><br><span class="line">(1) frameworks&#x2F;base&#x2F;libs&#x2F;hwui&#x2F;pipeline&#x2F;skia  -- SkiaOpenGLPipeline::getFrame()  ----&gt;</span><br><span class="line">frameworks&#x2F;base&#x2F;libs&#x2F;hwui&#x2F;renderthread&#x2F;EglManager.cpp - EglManager::beginFrame ----&gt;</span><br><span class="line">(1.1)EglManager::makeCurrent  -----&gt;</span><br><span class="line">frameworks&#x2F;native&#x2F;opengl&#x2F;libs&#x2F;EGL&#x2F;eglApi.cpp -- eglMakeCurrent ---&gt;</span><br><span class="line">frameworks&#x2F;native&#x2F;libs&#x2F;nativewindow&#x2F;ANativeWindow.cpp -- ANativeWindow_dequeueBuffer ----&gt;</span><br><span class="line">frameworks&#x2F;native&#x2F;libs&#x2F;gui&#x2F;Surface.cpp -- Surface::dequeueBuffer (&#96;systrace: HWC release&#96;)</span><br><span class="line"></span><br><span class="line">(1.2)EglManager::queryBufferAge</span><br><span class="line">----</span><br><span class="line">(2) SkiaOpenGLPipeline::draw()   ----&gt;</span><br><span class="line">(2.1) EglManager::damageFrame</span><br><span class="line">(2.2) SkiaPipeline::renderFrame (&#96;systrace:flush commands&#96; 后调用，systrace显示在后)[---&gt;SkiaPipeline::renderOverdraw]  ---&gt;</span><br><span class="line">SkiaPipeline::renderFrameImpl (先调用，systrace显示在前)  ---&gt;</span><br><span class="line">RenderNodeDrawable::onDraw  ----&gt;</span><br><span class="line">RenderNodeDrawable::forceDraw  [------&gt;RenderNodeDrawable::drawBackwardsProjectedNodes] ----&gt; </span><br><span class="line">RenderNodeDrawable::drawContent   ----&gt;</span><br><span class="line">RenderNodeDrawable::setViewProperties (&#96;systrace:alpha caused saveLayer&#96;)</span><br><span class="line">----</span><br><span class="line">(3) SkiaOpenGLPipeline::swapBuffers   -----&gt;</span><br><span class="line">frameworks&#x2F;native&#x2F;opengl&#x2F;libs&#x2F;EGL&#x2F;eglApi.cpp -- eglSwapBuffersWithDamageKHR   ----&gt;</span><br><span class="line">frameworks&#x2F;native&#x2F;libs&#x2F;nativewindow&#x2F;ANativeWindow.cpp -- ANativeWindow_queueBuffer  ---&gt;</span><br><span class="line">frameworks&#x2F;native&#x2F;libs&#x2F;gui&#x2F;Surface.cpp -- Surface::queueBuffer  (&#96;systrace: gpu completion&#96;绘制完成)</span><br></pre></td></tr></table></figure><hr><h2 id="合成送显流程"><a href="#合成送显流程" class="headerlink" title="合成送显流程"></a>合成送显流程</h2><blockquote><p>Android Q，非最新AOSP流程，有变动</p></blockquote><h3 id="SurfaceFlinger模块"><a href="#SurfaceFlinger模块" class="headerlink" title="SurfaceFlinger模块"></a>SurfaceFlinger模块</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">SurfaceFlinger::handleMessageRefresh();</span><br><span class="line">  preComposition();</span><br><span class="line">  rebuildLayerStacks();</span><br><span class="line">  calculateWorkingSet();</span><br><span class="line">  doComposition();</span><br><span class="line">    doDisplayComposition();</span><br><span class="line">    postFramebuffer();</span><br><span class="line">      HWComposer::presentAndGetReleaseFences();</span><br><span class="line">        HWC2::Display::present();</span><br><span class="line">          Composer::presentDisplay();</span><br></pre></td></tr></table></figure><h3 id="HWC模块"><a href="#HWC模块" class="headerlink" title="HWC模块"></a>HWC模块</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">HWCSession::PresentDisplay();</span><br><span class="line">  HWCDisplayBuiltIn::Present();</span><br><span class="line">    HWCDisplayBuiltIn::CommitLayerStack();</span><br><span class="line">      HWCDisplay::CommitLayerStack();</span><br><span class="line">        DisplayBuiltIn::Commit();</span><br><span class="line">          DisplayBase::Commit();</span><br><span class="line">            HWPeripheralDRM::Commit();</span><br><span class="line">               HWDeviceDRM::Commit();</span><br><span class="line">                 HWDeviceDRM::AtomicCommit();</span><br><span class="line">                   DRMAtomicReq::Commit();</span><br><span class="line">                     drmModeAtomicCommit();</span><br></pre></td></tr></table></figure><h3 id="DRM模块"><a href="#DRM模块" class="headerlink" title="DRM模块"></a>DRM模块</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><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">msm_atomic_commit();</span><br><span class="line">  msm_atomic_commit_dispatch();</span><br><span class="line">    kthread_queue_work(commit-&gt;commit_work);</span><br><span class="line"> </span><br><span class="line">_msm_drm_commit_work_cb();</span><br><span class="line">  complete_commit();</span><br><span class="line">    drm_atomic_helper_commit_planes();</span><br><span class="line">      sde_crtc_atomic_begin();</span><br><span class="line">        _sde_crtc_setup_mixers();</span><br><span class="line">        _sde_crtc_blend_setup();</span><br><span class="line">          _sde_crtc_blend_setup_mixer();</span><br><span class="line">            _sde_crtc_setup_blend_cfg(); &#x2F;&#x2F; blend_op</span><br><span class="line">        _sde_crtc_dest_scaler_setup();</span><br><span class="line">      sde_plane_atomic_update();</span><br><span class="line">        sde_plane_sspp_atomic_update();</span><br><span class="line">      sde_crtc_atomic_flush();</span><br><span class="line">        sde_plane_flush();</span><br><span class="line">    msm_atomic_helper_commit_modeset_enables();</span><br><span class="line">      sde_crtc_enable();</span><br><span class="line">      sde_encoder_virt_enable();</span><br><span class="line">        sde_encoder_resource_control(SDE_ENC_RC_EVENT_KICKOFF);</span><br><span class="line">        _sde_encoder_virt_enable_helper();</span><br><span class="line">          hw_mdptop-&gt;ops.reset_ubwc();</span><br><span class="line">          hw_ctl-&gt;ops.setup_intf_cfg_v1();</span><br><span class="line">          _sde_encoder_update_vsync_source();</span><br><span class="line">            sde_encoder_phys_cmd_setup_vsync_source();</span><br><span class="line">              sde_hw_intf_vsync_sel();</span><br><span class="line">          sde_encoder_control_te();</span><br><span class="line">            sde_encoder_phys_cmd_connect_te();</span><br><span class="line">              sde_hw_intf_connect_external_te();</span><br><span class="line">      sde_kms_commit();</span><br><span class="line">        sde_crtc_commit_kickoff();</span><br><span class="line">          sde_encoder_prepare_for_kickoff();</span><br><span class="line">      drm_bridge_enable();</span><br><span class="line">        dsi_bridge_enable();</span><br><span class="line">    msm_atomic_wait_for_commit_done();</span><br><span class="line">      sde_kms_wait_for_commit_done();</span><br><span class="line">        sde_encoder_wait_for_event(MSM_ENC_COMMIT_DONE);</span><br><span class="line">        sde_crtc_complete_flip();</span><br><span class="line">          drm_crtc_send_vblank_event();</span><br></pre></td></tr></table></figure><hr><h2 id="Fence同步机制"><a href="#Fence同步机制" class="headerlink" title="Fence同步机制"></a>Fence同步机制</h2><p><code>DummySwSyncTimeline CreateDummyFence()</code>创建软件模拟fence</p><p><strong>核心只有两件事情：</strong></p><ol><li>sw_sync_fence_create 创建一个软件模拟的fence</li><li>sw_sync_timeline_inc sync_timeline 时间轴向后推一个时间同步点</li></ol><p>BufferSlot对应的BufferItem中的mFence对象就是Fence类，最后会在setUpHwcComposer中<code>prepareFrame</code>设置到Hal中</p><p>在Android Framework中有一个用于操作fence同步栅的类（libsync中sync_wait类）</p><hr><h2 id="Google平台帧率切换"><a href="#Google平台帧率切换" class="headerlink" title="Google平台帧率切换"></a>Google平台帧率切换</h2><blockquote><p>参考：<a href="https://blog.csdn.net/u014535072/article/month/2020/05" target="_blank" rel="noopener">Android UI架构四、五</a><br>参考：<a href="https://blog.csdn.net/u014535072/article/month/2020/09" target="_blank" rel="noopener">Android UI架构十、十一</a></p></blockquote><p><strong>关键函数：</strong></p><ul><li><p>setFrameRate</p></li><li><p>getRefreshRate</p></li><li><p>app:    用于接收vsync信号并且上报给App进程，App开始画图    属性名：VSYNC_EVENT_PHASE_OFFSET_NS</p></li><li><p>sf:    用于SurfaceFlinger接收vsync信号用于渲染    属性名：SF_VSYNC_EVENT_PHASE_OFFSET_NS</p></li></ul><hr><h2 id="磁盘空间占满100"><a href="#磁盘空间占满100" class="headerlink" title="磁盘空间占满100%"></a>磁盘空间占满100%</h2><ol><li>使用命令 <code>sudo du -sh *</code> 查看各个目录的占内存情况，使用<code>du -h -x --max-depth=1</code>查看哪个深度1的目录占用过高</li><li>如果发现<code>/usr/local/tomcat</code>的目录占用的磁盘空间特别大,则进入该目录，发现logs的目录特别大,Tomcat的日志太大,可以将其删除。比如本地电脑发现<code>~/.cache/vmware</code>目录下的drag_and_drop文件很大，这是因为虚拟机和主系统之间每次拖拉文件，都会生成一个复制文件，造成该文件越来越大，可以删除。</li><li>再使用<code>df -h</code>发现磁盘空间</li></ol><h2 id="Android设备分辨率、密度以及dp的理解"><a href="#Android设备分辨率、密度以及dp的理解" class="headerlink" title="Android设备分辨率、密度以及dp的理解"></a>Android设备分辨率、密度以及dp的理解</h2><blockquote><p>参考：<a href="https://www.longdw.com/2013/04/18/android-px-dp/" target="_blank" rel="noopener">Android设备分辨率、密度以及dp的理解</a><br>参考：<a href="https://www.zcool.com.cn/article/ZNjI3NDQ=.html" target="_blank" rel="noopener">UI设计师不可不知的安卓屏幕知识</a></p></blockquote><ul><li>dpi计算方法：</li></ul><ol><li>分辨率的平方和开方（勾股定理） <code>adb shell wm size</code></li><li>然后除以屏幕的物理英寸，即是dpi</li></ol><p><code>adb shell wm density</code>可以查看当前的dpi（一般有的手机会在计算的结果上调整）</p><ul><li>dp计算方法（开发者选项最小宽度dp）</li></ul><p>1.<code>在安卓中，系统密度为160dpi的中密度手机屏幕为基准屏幕，即320×480的手机屏幕。在这个屏幕中，1dp=1px</code><br>2.分辨率的宽高分别除以dpi，获取到<code>sw320dp,h533dp</code>值</p><p>可通过<code>adb shell dumpsys window</code>查看到</p><h2 id="低内存分析方向和技巧"><a href="#低内存分析方向和技巧" class="headerlink" title="低内存分析方向和技巧"></a>低内存分析方向和技巧</h2><blockquote><p>参考：<a href="https://wizzie.top/Blog/2020/08/06/2020/200806_android_ANR_BaseLog/" target="_blank" rel="noopener">Android ANR基本Log分析</a></p></blockquote><blockquote><p>参考：<a href="https://mp.weixin.qq.com/s/z5Tlh5KwFFaVpJWrU2IRAA" target="_blank" rel="noopener">Android 系统内存耗用：VSS/RSS/PSS/USS 的介绍</a></p></blockquote><p><strong>dumpsys meminfo:</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">Total RAM: 2,697,524K (status normal)</span><br><span class="line"> Free RAM: 1,063,563K (  252,355K cached pss +   717,200K cached kernel +         0K cached ion +    94,008K free)</span><br><span class="line">      ION:   189,688K (    7,624K mapped +   182,064K unmapped +         0K pools)</span><br><span class="line"> totalSwapPss:   775,324K kernelCached:   272,476K</span><br><span class="line"> Used RAM: 2,677,904K (2,090,100K used pss +   587,804K kernel)</span><br><span class="line"> Lost RAM:   -23,995K</span><br><span class="line">     ZRAM:   200,100K physical used for   669,848K in swap (1,572,860K total swap)</span><br><span class="line">   Tuning: 128 (large 512), oom   322,560K, restore limit   107,520K (high-end-gfx)</span><br><span class="line">--------- 14.816s was the duration of dumpsys meminfo, ending at: 2021-01-05 16:09:23</span><br></pre></td></tr></table></figure><h3 id="查看进程占用内存情况"><a href="#查看进程占用内存情况" class="headerlink" title="查看进程占用内存情况"></a>查看进程占用内存情况</h3><p><code>adb shell procrank</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">  PID       Vss      Rss      Pss      Uss     Swap    PSwap    USwap    ZSwap  cmdline</span><br><span class="line"> 1195  9776476K  509716K  228733K  166648K    3748K     405K     352K      78K  system_server</span><br><span class="line"> 5466  7005964K  351036K  139327K  123836K    3404K      53K       0K      10K  com.android.systemui</span><br><span class="line">.....</span><br></pre></td></tr></table></figure><ul><li>VSS： 一个进程总共可访问的地址空间。其大小还包括了可能不在RAM中的内存（比如虽然malloc分配了空间，但尚未写入）。 VSS 很少被用于判断一个进程的真实内存使用量</li><li>RSS： 一个进程在RAM中真实存储的总内存。<strong>仅仅表示该进程所使用的所有共享库的大小，而不管有多少个进程使用该共享库，该共享库仅被加载到内存一次。所以RSS并不能准确反映单进程的内存占用情况</strong></li><li>PSS： 按比例表示使用的共享库。系统中所有进程的PSS都相加的话，就刚好反映了系统中的总共占用的内存。而当一个进程被销毁之后， 其占用的共享库那部分比例的PSS，将会再次按比例分配给余下使用该库的进程</li><li>USS： 进程所占用的私有内存（内存泄漏的最佳观察数据）。不包含so库占用内存的</li></ul><hr><h2 id="ubuntu查看公网ip"><a href="#ubuntu查看公网ip" class="headerlink" title="ubuntu查看公网ip"></a>ubuntu查看公网ip</h2><p>三种方法：</p><ol><li><code>curl www.icanhazip.com</code></li><li><code>curl ifconfig.me</code></li><li><code>curl -4sSkL https://myip.ipip.net</code></li></ol><hr><h2 id="查看视频解码的分辨率及对应的帧率支持"><a href="#查看视频解码的分辨率及对应的帧率支持" class="headerlink" title="查看视频解码的分辨率及对应的帧率支持"></a>查看视频解码的分辨率及对应的帧率支持</h2><p>日志关键词<code>MediaCodec</code></p><p>配置文件：<code>/vendor/etc/media_codecs.xml</code></p><hr><h2 id="linux和windows书签同步"><a href="#linux和windows书签同步" class="headerlink" title="linux和windows书签同步"></a>linux和windows书签同步</h2><p>about:config网址搜索sync：</p><ul><li><code>identity.sync.tokenserver.uri</code>：</li></ul><p>修改：</p><ul><li><p>linux：<code>https://sync.firefox.com.cn/token/1.0/sync/1.5</code></p></li><li><p>windows：<code>https://token.services.mozilla.com/1.0/sync/1.5</code></p></li></ul><hr><h2 id="getService配置权限"><a href="#getService配置权限" class="headerlink" title="getService配置权限"></a>getService配置权限</h2><p>首先getService获取服务，紧接着就是getTransport的接口调用。如果在配置文件里面没找到我们所要求的服务，就会报错并结束服务的获取</p><ul><li>首先会去<code>system/etc/vintf/manifest.xm</code>l文件去找我们调用者的服务名，如果找到就返回。</li><li>如果没找到就会去<code>vendor/etc/vintf/manifest.xml</code>文件去找，如果找到就返回。如果没找到则返回NULLPRT。同时打印<code>Cannot find entry xxx in either framework or device manifest.</code></li></ul><p>所以，如果碰到这样的log，首先要去检测这两个文件里面有没有配置我们调用的服务，没有就要自己配置。</p><p>一般是没有配置引起的，当然也有可能是xml增加的语法有问题，比如格式对齐等。</p><hr><h2 id="HIDL编写-升级流程"><a href="#HIDL编写-升级流程" class="headerlink" title="HIDL编写/升级流程"></a>HIDL编写/升级流程</h2><blockquote><p>参考：<a href="https://www.cnblogs.com/caidi/p/13214913.html" target="_blank" rel="noopener">VINTF</a><br>参考：<a href="http://ddrv.cn/a/238197" target="_blank" rel="noopener">VINTF简介</a><br>参考：<a href="https://source.android.google.cn/devices/architecture/vintf" target="_blank" rel="noopener">供应商接口对象</a></p></blockquote><p>总体流程:</p><ol><li>创建HIDL接口文件<code>XXX.hal</code>：HIDL为每个HAL模块设计了不同接口定义hal文件，以<code>.hal</code>结尾，在<code>hidl-gen</code>工具的帮助下即可自动编译生成对应接口C++实现或者Java实现</li><li><code>hidl-gen</code>生成<code>interface hash</code>, 添加至current.txt</li><li><code>hidl-gen</code>生成接口,更新<code>接口, XXX.bp, XXX.mk</code>等</li><li>更新vendor manifest文件：对于TA开发来说, 底层TA是服务提供者(需求提供者), 因此提供的是manifest(vendor), 将<code>vintf/manifests.xml</code>push到设备端:<code>adb push manifest.xml /vendor/etc/vintf</code>；system(framwork是发起需求者,需要设什么样的需求), 这部分内容在新平台上也在<code>vintf/manifests.xml</code>，push到设备端:<code>adb push compatibity.device.xml  /system/etc/vintf</code></li><li>更新fcm(matrix)，即compatibility matrix XML编写：manifest文件描述了提供给对方的feature, Matrix描述了需要对方提供什么样的feature. Manifest和Matrix在OTA升级之前会进行匹配检查, 确保framework和device是兼容的, 总的来说,manifest是feature提供端, matrix是feature需求端</li></ol><ul><li>关于compatibility Matrices：</li><li><ul><li><code>framework compatibility matrix</code>：描述了framwork对device的需求, 这个matrix文件是和system.img关联的, 这些需求需要被device manifest支持</li></ul></li><li><ul><li><code>device compatibility matrix</code>：描述了device对framework的需求</li></ul></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">//xml manifest语法</span><br><span class="line"><span class="tag">&lt;<span class="name">manifest</span> <span class="attr">version</span>=<span class="string">"1.0"</span> <span class="attr">type</span>=<span class="string">"device"</span>&gt;</span>   //version: 必选, manifest的主版本号；type: 必选, manifest的类型,属于vendor,值为device; 属于system,值为framework</span><br><span class="line">    <span class="tag">&lt;<span class="name">hal</span> <span class="attr">format</span>=<span class="string">"hidl"</span>&gt;</span>    //hal:可选,可以存在多个,代表HAL(HIDL或者native); format:可选, 值为hidl(默认),或者native,分别代表HIDL和native HALS</span><br><span class="line">        <span class="tag">&lt;<span class="name">name</span>&gt;</span>vendor.android.hardware.test<span class="tag">&lt;/<span class="name">name</span>&gt;</span>  // name 必选, 代表HAL的合规包名</span><br><span class="line">        <span class="tag">&lt;<span class="name">transport</span>&gt;</span>hwbinder<span class="tag">&lt;/<span class="name">transport</span>&gt;</span>  // 和format有关,format的值是hidl时，就是必选的，否则不必存在.申明了当这个包的接口被service manager查询时将使用何种transport方式</span><br><span class="line">                                         // 可以是hwbinder,代表绑定式hidl, 也可以是passthrough: 代表直通式hide</span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span>  //必选, 代表当前hidl的版本号</span><br><span class="line">        <span class="tag">&lt;<span class="name">interface</span>&gt;</span>             // 必选的,可以多个但是不能重复,声明了一个含有实例的接口</span><br><span class="line">            <span class="tag">&lt;<span class="name">name</span>&gt;</span>ITestService<span class="tag">&lt;/<span class="name">name</span>&gt;</span> //必选  接口实例的名字</span><br><span class="line">            <span class="tag">&lt;<span class="name">instance</span>&gt;</span>default<span class="tag">&lt;/<span class="name">instance</span>&gt;</span> //必选,不可重复</span><br><span class="line">        <span class="tag">&lt;/<span class="name">interface</span>&gt;</span></span><br><span class="line">        .....</span><br></pre></td></tr></table></figure><p>结构层级:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">manifest:version  type &#x2F;&#x2F;这一层级申明当前manifest</span><br><span class="line">|--hal format 第二层级</span><br><span class="line">    |--name</span><br><span class="line">    |--transtport</span><br><span class="line">    |--version</span><br><span class="line">    |-- interface</span><br><span class="line">      |-- name</span><br><span class="line">      |-- instance</span><br><span class="line">|--sepolicy</span><br><span class="line">  |--version</span><br></pre></td></tr></table></figure><ol start="6"><li>启动服务:首先需要将生成的库文件和服务守护进程分别push到设备端, 其次, 使用<code>lshal</code>查看对应的服务是否启动,如果为启动,需要查看对应的rc文件是否存在, 若不存在,则<code>push(vendor/etc/xxx.rc)</code>, 手动启服务</li></ol><ul><li><p>概念：hidl接口哈希,是一种旨在防止意外更改接口,并确保接口更改经过全面审查的机制.因为HIDL接口带有版本号, 因此,接口一旦发布就bade在更改</p></li><li><p>布局：每个软件包根目录,都必须包含一个列出所有已经发布的HIDL接口的current.txt文件</p></li><li><p>使用hidl-gen添加哈希：hidl-gen生成的每个接口定义库都包含哈希，通过调用<code>IBase::getHashChain</code>可检索这些哈希。hidl-gen 编译接口时，会检查HAL软件包根目录中的current.txt 文件，以查看HAL是否已被更改</p></li><li><ul><li>如果没有找到HAL的哈希，则接口会被视为未发布（处于开发阶段），并且编译会继续进行</li></ul></li><li><ul><li>如果找到了相应哈希，则会对照当前接口对其进行检查</li></ul></li></ul><p>如果接口与哈希匹配，则编译会继续进行</p><p>如果接口与哈希不匹配，则编译会暂停，因为这意味着之前发布的接口会被更改</p><p>如要进行保留ABI的更改（ABI稳定性），必须先修改current.txt文件，然后编译才能继续进行</p><p>所有其他更改都应在接口的minor或major版本升级中进行</p><p>Tips：</p><ul><li>查看HIDL编写的hal服务是否启动：<code>adb shell lshal</code></li></ul><ol start="7"><li>重启</li></ol><h2 id="ssk-key生成current-txt-hash值"><a href="#ssk-key生成current-txt-hash值" class="headerlink" title="ssk-key生成current.txt hash值"></a>ssk-key生成current.txt hash值</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ hidl-gen -L hash -r vendor.....Testservice（service名称）:&#x2F;vendor&#x2F;...&#x2F;interfaces&#x2F;Testservice（路径） -r android.hidl:system&#x2F;libhidl&#x2F;transport vendor...Testservice@1.0</span><br><span class="line"></span><br><span class="line">e147fb59c6ffd19c8ea7345423e6b99d8d450e6078ff40367122a9c4ee005da5 vendor...Testservice@1.0::types</span><br><span class="line">8f202376e0d66fa4c9448d71415993994de99150b7326e32ac5a04b4b94aec09 vendor....Testservice@1.0::ITestQservice</span><br></pre></td></tr></table></figure><hr><h2 id="修改git-remote-v的orign源"><a href="#修改git-remote-v的orign源" class="headerlink" title="修改git remote -v的orign源"></a>修改git remote -v的orign源</h2><ol><li><code>git remote -v</code>查看远程提交信息</li><li><code>git remote rm origin</code>删除</li><li>添加：<code>git remote add origin (正确的信息)ssh://..../platform/frameworks/native</code></li><li><code>git remote -v</code>查看，如果push未修改成功没有关系</li><li>提交代码，记得加上<code>-u</code>选项：<code>git push -u orign master</code></li></ol><hr><h2 id="git报错无法push（不建议使用）"><a href="#git报错无法push（不建议使用）" class="headerlink" title="git报错无法push（不建议使用）"></a>git报错无法push（不建议使用）</h2><p>报错信息：</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">fatal: Unpack error, check server log</span><br><span class="line">error: remote unpack failed: error Missing tree 7d3329b06421654faca962eab6cb6f9da6b47b9f</span><br></pre></td></tr></table></figure><p>使用：<code>git push --no-thin origin dev</code></p><h2 id="git-commit修改作者名称"><a href="#git-commit修改作者名称" class="headerlink" title="git commit修改作者名称"></a>git commit修改作者名称</h2><p><code>git commit --amend --author=&quot;wizzie &lt;wizzie@test.com&gt;&quot;</code></p><h2 id="gerrit拉取Andorid指定模块代码"><a href="#gerrit拉取Andorid指定模块代码" class="headerlink" title="gerrit拉取Andorid指定模块代码"></a>gerrit拉取Andorid指定模块代码</h2><ol><li>repo init</li><li>gerrit提交代码的界面projects里面搜索要拉取的指定模块</li><li>复制<code>git clone</code>然后本地执行</li><li><code>git branch -a</code>查看所有分支，然后<code>git checkout 分支名</code>（不包含orign/master）</li><li>git pull</li></ol><hr><h2 id="ubuntu命令查看dns"><a href="#ubuntu命令查看dns" class="headerlink" title="ubuntu命令查看dns"></a>ubuntu命令查看dns</h2><p><code>nm-tool</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">- Device: eth0  [Auto Ethernet] ------------------------------------------------</span><br><span class="line">  Type:              Wired</span><br><span class="line">  Driver:            e1000e</span><br><span class="line">  State:             connected</span><br><span class="line">  Default:           yes</span><br><span class="line">  HW Address:        ...:5D</span><br><span class="line"></span><br><span class="line">  Capabilities:</span><br><span class="line">    Carrier Detect:  yes</span><br><span class="line">    Speed:           1000 Mb&#x2F;s</span><br><span class="line"></span><br><span class="line">  Wired Properties</span><br><span class="line">    Carrier:         on</span><br><span class="line"></span><br><span class="line">  IPv4 Settings:</span><br><span class="line">    Address:         ....241</span><br><span class="line">    Prefix:          24 (255.255.255.0)</span><br><span class="line">    Gateway:         ....1</span><br><span class="line"></span><br><span class="line">    DNS:             ....74</span><br><span class="line">    DNS:             ....75</span><br><span class="line">    DNS:             ....73</span><br></pre></td></tr></table></figure><h2 id="markdown数学公式语法"><a href="#markdown数学公式语法" class="headerlink" title="markdown数学公式语法"></a>markdown数学公式语法</h2><blockquote><p>参考：<a href="https://www.jianshu.com/p/e74eb43960a1" target="_blank" rel="noopener">Markdown数学公式语法</a></p></blockquote><ul><li><p>行内公式：将公式插入到本行内，符号：<code>$公式内容$</code>，如：$xyz$</p></li><li><p>独行公式：将公式插入到新的一行内，并且居中，符号：<code>$$公式内容$$</code>，如：$$xyz$$</p></li><li><p>上标符号，符号：^，如：$x^4$</p></li><li><p>下标符号，符号：_，如：$x_1$</p></li><li><p>组合符号，符号：{}，如：${16}<em>{8}O{2+}</em>{2}$</p></li></ul><h2 id="安装adb和fastboot"><a href="#安装adb和fastboot" class="headerlink" title="安装adb和fastboot"></a>安装adb和fastboot</h2><p><code>sudo apt-get install android-tools-adb</code></p><p><code>sudo apt-get install android-tools-fastboot</code></p><p>或者使用Android SDK/platform-tools</p><h2 id="针对image编译"><a href="#针对image编译" class="headerlink" title="针对image编译"></a>针对image编译</h2><ol><li>编译所有image</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">source build&#x2F;envsetup.sh</span><br><span class="line">lunch XX-userdebug</span><br><span class="line">.&#x2F;build.sh dist -j4</span><br></pre></td></tr></table></figure><ol start="2"><li>编译system.img，生成在qssi目录下</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">source build&#x2F;envsetup.sh</span><br><span class="line">lunch xx-userdebug</span><br><span class="line">.&#x2F;build.sh dist qssi_only -j4</span><br></pre></td></tr></table></figure><ol start="3"><li>编译super.img</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">source build&#x2F;envsetup.sh</span><br><span class="line">lunch xx-userdebug</span><br><span class="line">.&#x2F;build.sh dist merge_only -j4</span><br></pre></td></tr></table></figure><ol start="4"><li>编译其它img，例如vendorimage（如果不指定会编译其它所有img）</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">source build&#x2F;envsetup.sh</span><br><span class="line">lunch xx-userdebug</span><br><span class="line">.&#x2F;build.sh vendorimage dist target_only -j4</span><br></pre></td></tr></table></figure><hr><h2 id="安装APK报错"><a href="#安装APK报错" class="headerlink" title="安装APK报错"></a>安装APK报错</h2><p>如果安装APK报错，控制台输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ adb install test.apk</span><br><span class="line">5451 KB&#x2F;s (635477 bytes in 0.113s)</span><br><span class="line">Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package com.debug.test signatures do not match previously installed version; ignoring!]</span><br></pre></td></tr></table></figure><p>需要先删除原先的APK，并且清楚缓存，重启：</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></pre></td><td class="code"><pre><span class="line">adb shell dumpsys activity top|grep ACTIVITY</span><br><span class="line">adb shell pm path com.debug.test</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;删除卸载原APK</span><br><span class="line">adb shell rm -rf ***</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;清楚缓存</span><br><span class="line">adb shell pm clear com.debug.test</span><br><span class="line">adb reboot</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;再次安装新的APK</span><br><span class="line">adb install test.apk</span><br></pre></td></tr></table></figure><hr><h2 id="查看当前前台app包名（实时）"><a href="#查看当前前台app包名（实时）" class="headerlink" title="查看当前前台app包名（实时）"></a>查看当前前台app包名（实时）</h2><p><code>adb shell dumpsys window | grep mCurrentFocus</code></p><h2 id="监控设备实时获取当前的包名"><a href="#监控设备实时获取当前的包名" class="headerlink" title="监控设备实时获取当前的包名"></a>监控设备实时获取当前的包名</h2><p><code>adb shell am monitor</code></p><h2 id="APP增加多国语言"><a href="#APP增加多国语言" class="headerlink" title="APP增加多国语言"></a>APP增加多国语言</h2><blockquote><p>参考：<a href="https://www.jianshu.com/p/530e033fca33" target="_blank" rel="noopener">让App支持不同的语言</a><br>参考：<a href="https://baike.baidu.com/item/%E8%AF%AD%E8%A8%80%E4%BB%A3%E7%A0%81" target="_blank" rel="noopener">语言代码</a><br>参考：<a href="https://www.cnblogs.com/zyw-205520/p/3848399.html" target="_blank" rel="noopener">Android多语言支持以及各国语言Values文件夹命名规则</a></p></blockquote><ol><li>目录在：<code>res/values-***</code></li><li>将各个语言区域的字符串值添加到相应文件中。在运行时，Android 系统会根据当前为用户设备设置的语言区域使用相应的字符串资源集的</li><li>如需添加对更多语言的支持，在<code>res/</code>目录下创建名称末尾不同的values，其中目录末尾ISO语言代码则对应着相应语言，譬如：<code>values-fr</code>则代表着手机系统语言为法语时读取的目录</li></ol><p>例如：</p><ul><li>英语（默认语言区域），<code>/values/strings.xml</code></li><li>中文，<code>/values-zh/strings.xml</code></li><li>法语，<code>values-fr/strings.xml</code></li></ul><hr><h2 id="native-crash分析"><a href="#native-crash分析" class="headerlink" title="native crash分析"></a>native crash分析</h2><p>使用addr2line命令定位堆栈地址：</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">&#x2F;out&#x2F;target&#x2F;product&#x2F;product&#x2F;symbols&#x2F;system&#x2F;lib64$ addr2line -f -e libgui.so 00000000000c39a0</span><br><span class="line">_ZNK7android12SortedVectorINS_12DisplayStateEE16do_move_backwardEPvPKvm</span><br></pre></td></tr></table></figure><p>解释：<code>-e</code>后加上.so的文件名；<code>-f</code>可同时输出函数名称</p><p>分析工具：</p><ul><li>addr2line：<code>addr2line –e obj/local/armeabi/libhello-jni.so 00004de8</code>, 用来分析单个pc地址对应的源码行数</li><li>objdump：<code>objdump –S obj/local/armeabi/libhello-jni.so &gt; hello.asm</code>,或者<code>symbols/out/target/product/merlin/symbols$ objdump -tT system/lib64/libdisplayfeature.so</code> 用来把相应的so变成汇编语言的asm文件，然后根据地址信息（比如00000000000c39a0）就可以找到更加详细的相关函数信息；</li><li>ndk-stack：用来把log信息全部翻译成更加详细的带源码行数信息的log，相当于是在整个crash堆栈信息都执行addr2line命令。</li></ul><p><code>addr2line -Cef .so address</code></p><p>或者：</p><ol><li>可以使用<code>readelf -a [.so/.bin]</code>解析库地址</li><li>根据解析结果查询函数，C++在linux系统编译后会变成类似<code>_ZNK...</code>的修饰名。使用<code>c++filt</code>获取函数的原始名称：</li></ol><p><code>c++filt [_ZNK...函数修饰名]</code></p><hr><h2 id="YUV数据查看"><a href="#YUV数据查看" class="headerlink" title="YUV数据查看"></a>YUV数据查看</h2><ol><li>从ffmpeg官网：<code>http://ffmpeg.org/download.html</code>下载最新的ffmpeg安装包，然后通过如下命令解压：<br><code>tar jxf ffmpeg-2.5.3.tar.bz2</code></li><li>查看YUV：<br><code>ffplay -f rawvideo -video_size 1280x800 raw_1280x800_1878.yuv</code></li></ol><p>或者使用YUVPlayer工具，执行：</p><p><code>wine yuvplayer.exe</code></p><p><strong>YUV Player工具获取：</strong></p><ul><li><a href="https://github.com/Yonsm/RawPlayer" target="_blank" rel="noopener">https://github.com/Yonsm/RawPlayer</a> RawPlayer</li><li><a href="https://github.com/latelee/YUVPlayer" target="_blank" rel="noopener">https://github.com/latelee/YUVPlayer</a> YUVPlayer</li><li><a href="https://github.com/figgis/yuv-viewer" target="_blank" rel="noopener">https://github.com/figgis/yuv-viewer</a>  yuv-viewer</li><li><a href="https://github.com/Luomingbear/YuvPlayer" target="_blank" rel="noopener">https://github.com/Luomingbear/YuvPlayer</a> android yuv播放器 SDL</li></ul><hr><h2 id="关闭硬件加速"><a href="#关闭硬件加速" class="headerlink" title="关闭硬件加速"></a>关闭硬件加速</h2><p>三种不同针对对象的方法：</p><ol><li>在AndroidManifest.xml中设置<code>android:hardwareAccelerated=&quot;false&quot;</code>，注意关闭整个app的硬件加速，慎用</li><li>View有个方法支持单独的View关闭硬件加速，可以设置<code>mView.setLaterType(View.LAYER_TYPE_SOFTWARE);</code></li><li>关闭某一个控件的硬件加速功能：<code>findViewById(R.id.btn).setLayerType(View.LAYER_TYPE_SOFTWARE,null);</code></li></ol><hr><h2 id="linux绘图UML-序列图工具dia"><a href="#linux绘图UML-序列图工具dia" class="headerlink" title="linux绘图UML/序列图工具dia"></a>linux绘图UML/序列图工具dia</h2><ol><li>安装：<code>sudo apt-get install dia</code></li><li>使用，终端执行：<code>dia</code></li></ol><hr><h2 id="屏幕旋转-转向切换命令"><a href="#屏幕旋转-转向切换命令" class="headerlink" title="屏幕旋转/转向切换命令"></a>屏幕旋转/转向切换命令</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">adb root</span><br><span class="line">#turn off the automatic rotation</span><br><span class="line">adb shell settings put system accelerometer_rotation 0</span><br><span class="line"># rotate landscape</span><br><span class="line">adb shell settings put system user_rotation 1</span><br><span class="line"># rotate portrait</span><br><span class="line">adb shell settings put system user_rotation 0</span><br></pre></td></tr></table></figure><hr><h2 id="Surface两种合成方式（dump-sf）"><a href="#Surface两种合成方式（dump-sf）" class="headerlink" title="Surface两种合成方式（dump sf）"></a>Surface两种合成方式（dump sf）</h2><ul><li><p>Client合成：Client合成方式是相对于硬件合成来说的，其合成方式是，将各个Layer的内容用GPU渲染到暂存缓冲区中，最后将暂存缓冲区传送到显示硬件。这个暂存缓冲区，我们称为FBTarget，每个Display设备有各自的FBTarget。Client合成，之前称为GLES合成，我们也可以称之为GPU合成。Client合成，采用RenderEngine进行合成</p></li><li><p>Device合成：就是用专门的硬件合成器进行合成HWComposer，所以硬件合成的能力就取决于硬件的实现。其合成方式是将各个Layer的数据全部传给显示硬件，并告知它从不同的缓冲区读取屏幕不同部分的数据。HWComposer是Devicehec的抽象。</p></li></ul><hr><h2 id="getprop查看当前版本是user-userdebug"><a href="#getprop查看当前版本是user-userdebug" class="headerlink" title="getprop查看当前版本是user/userdebug"></a>getprop查看当前版本是user/userdebug</h2><p><code>adb shell getprop ro.system.build.type</code></p><h2 id="getprop查看设备信息"><a href="#getprop查看设备信息" class="headerlink" title="getprop查看设备信息"></a>getprop查看设备信息</h2><p><code>adb shell getprop ro.build.product</code></p><hr><h2 id="滑动到边缘Pixel值调试"><a href="#滑动到边缘Pixel值调试" class="headerlink" title="滑动到边缘Pixel值调试"></a>滑动到边缘Pixel值调试</h2><p>AOSP源码位置：<code>/frameworks/base/core/java/com/android/internal/widget/PointerLocationView.java</code>的onDraw函数中有<code>ps.mCoords.x</code>和<code>ps.mCoords.y</code>（float类型），可以将其打印出来，如果有问题可以修改此处转换逻辑</p><hr><h2 id="Input调试（PointerLocation）"><a href="#Input调试（PointerLocation）" class="headerlink" title="Input调试（PointerLocation）"></a>Input调试（PointerLocation）</h2><h3 id="systrace查看"><a href="#systrace查看" class="headerlink" title="systrace查看"></a>systrace查看</h3><p>systrace可以查看到touch点击的事件</p><h3 id="点击input事件-up-down"><a href="#点击input事件-up-down" class="headerlink" title="点击input事件(up/down)"></a>点击input事件(up/down)</h3><p><code>adb shell getevent -lrt</code></p><p>查看帮助：<br><code>adb shell getevent -h</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">[    1423.973137] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_TRACKING_ID   0000003b            </span><br><span class="line">[    1423.973137] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_POSITION_X    0000017e            &#x2F;&#x2F;十六进制转成十进制 1*16*16+7*16+14*1&#x3D;382   横坐标X</span><br><span class="line">[    1423.973137] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_POSITION_Y    0000032d            &#x2F;&#x2F;3*16*16+2*16+13*1&#x3D;813  纵坐标Y</span><br><span class="line">[    1423.973137] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_TOUCH_MAJOR   0000000a            </span><br><span class="line">[    1423.973137] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_PRESSURE      000003e8            </span><br><span class="line">[    1423.973137] &#x2F;dev&#x2F;input&#x2F;event2: EV_KEY       BTN_TOUCH            DOWN                </span><br><span class="line">[    1423.973137] &#x2F;dev&#x2F;input&#x2F;event2: EV_SYN       SYN_REPORT           00000000            </span><br><span class="line">[    1436.084174] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_TOUCH_MAJOR   00000000            </span><br><span class="line">[    1436.084174] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_PRESSURE      00000000            </span><br><span class="line">[    1436.084174] &#x2F;dev&#x2F;input&#x2F;event2: EV_ABS       ABS_MT_TRACKING_ID   ffffffff            </span><br><span class="line">[    1436.084174] &#x2F;dev&#x2F;input&#x2F;event2: EV_KEY       BTN_TOUCH            UP                  </span><br><span class="line">[    1436.084174] &#x2F;dev&#x2F;input&#x2F;event2: EV_SYN       SYN_REPORT           00000000             rate 0</span><br></pre></td></tr></table></figure><h3 id="input和wms的debug-log开关打开"><a href="#input和wms的debug-log开关打开" class="headerlink" title="input和wms的debug log开关打开"></a>input和wms的debug log开关打开</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">adb root</span><br><span class="line">&#x2F;&#x2F;wns debug</span><br><span class="line">adb shell dumpsys window -d enable DEBUG_FOCUS</span><br><span class="line">adb shell dumpsys window -d enable DEBUG_INPUT</span><br><span class="line">&#x2F;&#x2F;或者</span><br><span class="line">adb shell dumpsys window -d enable a</span><br><span class="line">&#x2F;&#x2F;input debug</span><br><span class="line">adb shell setprop sys.inputlog.enabled true</span><br><span class="line">adb shell setprop sys.input.TouchFilterEnable true</span><br><span class="line">adb shell setprop sys.input.TouchFilterLogEnable true</span><br><span class="line">adb shell dumpsys input</span><br></pre></td></tr></table></figure><hr><h2 id="FPS帧率测试"><a href="#FPS帧率测试" class="headerlink" title="FPS帧率测试"></a>FPS帧率测试</h2><p>查看设备的帧率：<code>while true;do adb shell dumpsys SurfaceFlinger | grep &quot;refresh-rate&quot;;done</code></p><h3 id="获取实时帧率（主要用于获取游戏-视频应用的fps数据）"><a href="#获取实时帧率（主要用于获取游戏-视频应用的fps数据）" class="headerlink" title="获取实时帧率（主要用于获取游戏/视频应用的fps数据）"></a>获取实时帧率（主要用于获取游戏/视频应用的fps数据）</h3><blockquote><p>参考前面的文章：<a href="https://wizzie.top/Blog/2020/03/31/2020/200330_android_getFPS/" target="_blank" rel="noopener">Android 两种实时获取FPS的方法</a></p></blockquote><ol><li>查询当前的layer（直接dump sf）：<code>adb shell dumpsys SurfaceFlinger --list</code></li><li><code>adb shell dumpsys SurfaceFlinger --latency SurfaceView</code></li></ol><h3 id="dump-gfxinfo信息"><a href="#dump-gfxinfo信息" class="headerlink" title="dump gfxinfo信息"></a>dump gfxinfo信息</h3><blockquote><p><code>adb shell dumpsys gfxinfo &lt;package_name&gt;</code></p></blockquote><p>查看每一帧的信息：<code>adb shell dumpsys gfxinfo &lt;package_name&gt; framestats</code></p><p><strong>结果：</strong></p><ul><li>Graphics info for pid 31148 [com.android.settings]: 表明当前dump的为设置界面的帧信息，pid为31148</li><li>Total frames rendered: 105 本次dump搜集了105帧的信息</li><li>Janky frames: 2 (1.90%) 105帧中有2帧的耗时超过了16ms，卡顿概率为1.9%</li><li>Number Missed Vsync: 0 垂直同步失败的帧</li><li>Number High input latency: 0 处理input时间超时的帧数</li><li>Number Slow UI thread: 2 因UI线程上的工作导致超时的帧数</li><li>Number Slow bitmap uploads: 0 因bitmap的加载耗时的帧数</li><li>Number Slow issue draw commands: 1 因绘制导致耗时的帧数</li><li>HISTOGRAM: 5ms=78 6ms=16 7ms=4 … 直方图数据</li></ul><p>例如：<code>while true;do adb shell dumpsys gfxinfo com.android.home framestat;sleep 1;done|tee swipeHome_dumpgfx.log</code></p><p>然后通过一秒抓一次，递减总帧数，确认每秒绘制的帧数:</p><ul><li><p>重置：<code>adb shell dumpsys gfxinfo com.android.home reset</code></p></li><li><p>抓取：<code>adb shell dumpsys gfxinfo com.android.home framestats</code></p></li></ul><h3 id="查看帧率以及如何在systrace分析帧率"><a href="#查看帧率以及如何在systrace分析帧率" class="headerlink" title="查看帧率以及如何在systrace分析帧率"></a>查看帧率以及如何在systrace分析帧率</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">adb root</span><br><span class="line">adb shell setprop debug.gr.calcfps 1</span><br><span class="line">adb shell setprop debug.gr.calcfps.period 5</span><br><span class="line">adb shell stop</span><br><span class="line">adb shell start</span><br></pre></td></tr></table></figure><p>之后可以在logcat中通过<code>fps</code>关键字查看帧率</p><p>Tips:</p><ol><li>该帧率为实时帧率，一个动画的首帧和尾帧存在误差，可以忽略</li><li><code>debug.gr.calcfps.period  n</code>:后面的数字n控制统计的帧数</li><li>重启手机后将不再统计帧率</li></ol><h3 id="systrace中的Frame"><a href="#systrace中的Frame" class="headerlink" title="systrace中的Frame"></a>systrace中的Frame</h3><p>在每个app进程，都有一个Frames行，正常情况以绿色的圆点表示。当圆点颜色为黄色或者红色时，意味着这一帧超过16.6ms（即发现丢帧），点击黄色或红色的Frames圆点便会有相关的提示信息；</p><p>另外，在systrace的最右侧，有一个<code>Alerts tab</code>可以展开，这里记录着所有的的警告提示信息；</p><p>也可以通过放大那一帧进一步分析问题。</p><p>对于Android 5.0(API level 21)或者更高的设备，该问题主要聚焦在<code>UI Thread</code>和<code>Render Thread</code>这两个线程当中</p><hr><h2 id="屏幕录制视频（限定时间长）"><a href="#屏幕录制视频（限定时间长）" class="headerlink" title="屏幕录制视频（限定时间长）"></a>屏幕录制视频（限定时间长）</h2><ul><li><code>adb shell screenrecord --time-limit 10 --verbose /sdcard/record.mp4</code></li></ul><p>录屏带<code>--bugreport</code>调试参数：</p><p><code>adb shell screenrecord --bugreport /sdcard/test.mp4</code></p><h3 id="截图命令"><a href="#截图命令" class="headerlink" title="截图命令"></a>截图命令</h3><p><code>adb shell screencap /sdcard/screencap.png</code></p><hr><h2 id="dump-sf查看buffer-layer列表"><a href="#dump-sf查看buffer-layer列表" class="headerlink" title="dump sf查看buffer/layer列表"></a>dump sf查看buffer/layer列表</h2><p><code>adb shell dumpsys SurfaceFlinger --list</code></p><h3 id="dump-windows-inpu-sf"><a href="#dump-windows-inpu-sf" class="headerlink" title="dump windows/inpu/sf"></a>dump windows/inpu/sf</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">adb root</span><br><span class="line">adb shell dumpsys input</span><br><span class="line">adb shell dumpsys window -a</span><br><span class="line">adb shell dumpsys SurfaceFlinger</span><br></pre></td></tr></table></figure><hr><h2 id="源码打开input-debug日志开关"><a href="#源码打开input-debug日志开关" class="headerlink" title="源码打开input debug日志开关"></a>源码打开input debug日志开关</h2><ul><li>frameworks/native/services/inputflinger/InputDispatcher.cpp：<code>bool gInputLogEnabled = false;</code>改为true</li><li>frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java：<code>static final boolean DEBUG_INPUT = false;</code></li></ul><hr><h2 id="adb打印kernel-log，即内核调试信息dmsg和kmsg"><a href="#adb打印kernel-log，即内核调试信息dmsg和kmsg" class="headerlink" title="adb打印kernel log，即内核调试信息dmsg和kmsg"></a>adb打印kernel log，即内核调试信息dmsg和kmsg</h2><blockquote><p>Android的log信息分为内核空间和用户空间中LOG，查看用户空间（也就是app等上层的log）的log直接用<code>adb logcat</code>就可以</p><p>如果想过滤一些信息就用<code>adb logcat | grep -E &quot;log_xxx|log_aaa”</code>命令，这样log只会显示含有log_aaa和log_xxx的log信息了。上层的log信息另行查看。</p></blockquote><ol><li><p>执行dmesg命令，可以查看全部消息。如果想把log信息保存到文件，可以用<code>adb shell dmesg &gt; E:/Kernel.log</code>  (输出内核信息)</p></li><li><p><code>adb shell cat /proc/kmsg</code>：程序里面用printk函数打印的信息会显示出来。</p></li></ol><p>用<code>cat /proc/kmsg</code>命令打印出来的信息跟与dmesg不同。第一次执行<code>/proc/kmsg</code>打印到当前时间的所有内核信息，再次执行<code>cat /proc/kmsg</code>，不会再打印已经打印的信息，只打印上一次执行之后打印出来的新的信息。</p><p>Tips:查看kernel log时间，前面是秒，然后隔一段时间会打印<code>android time</code>标记日志</p><h3 id="合并kmsg-log"><a href="#合并kmsg-log" class="headerlink" title="合并kmsg log"></a>合并kmsg log</h3><blockquote><p><code>adb shell logcat -v time -f /dev/kmsg | adb shell cat /proc/kmsg | tee kernel_logcat.txt</code></p></blockquote><ul><li>“-f”选项 : 该选向后面跟着输入日志的文件, 使用<code>adb logcat -f /sdcard/log.txt</code>命令, 注意这个log文件是输出到手机上，需要指定合适的路径</li></ul><p>例如触屏inpu事件log抓取：</p><p><code>adb shell logcat -v threadtime -f /dev/kmsg | adb shell cat /proc/kmsg | grep -Ei &quot;InputDispatcher|DOWN\!|Up\!|ViewRootImpl|onTouchEvent&quot;</code></p><hr><h2 id="查看设备的白名单"><a href="#查看设备的白名单" class="headerlink" title="查看设备的白名单"></a>查看设备的白名单</h2><ul><li><code>adb shell cat vendor/etc/power_whitelist_cfg.xml</code></li></ul><p>代码添加示例：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">//vendor/PLATFORM/proprietary/hardware/power/config/平台型号/power_whitelist_cfg.xml</span><br><span class="line">        <span class="tag">&lt;<span class="name">Package</span> <span class="attr">name</span>=<span class="string">"com.android.gallery"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">Activity</span> <span class="attr">name</span>=<span class="string">"Common"</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">PERF_RES_SCHED_BOOST_VALUE_TA</span> <span class="attr">Param1</span>=<span class="string">"10"</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">PERF_RES_THERMAL_POLICY</span> <span class="attr">Param1</span>=<span class="string">"8"</span>/&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">PERF_RES_DRAM_CM_MGR</span> <span class="attr">Param1</span>=<span class="string">"1"</span>/&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">Activity</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">Package</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="Android-mk和Android-bp转换语法"><a href="#Android-mk和Android-bp转换语法" class="headerlink" title="Android.mk和Android.bp转换语法"></a>Android.mk和Android.bp转换语法</h2><p>相互转换的定义在：</p><figure class="highlight plain"><figcaption><span>/build/soong/androidmk/cmd/androidmk/android.go</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">&#x2F;&#x2F;该代码Android Q AOSP，在R之后该文件有变化</span><br><span class="line">func init() &#123;</span><br><span class="line">addStandardProperties(bpparser.StringType,</span><br><span class="line">map[string]string&#123;</span><br><span class="line">&quot;LOCAL_MODULE&quot;:                  &quot;name&quot;,</span><br><span class="line">&quot;LOCAL_CXX_STL&quot;:                 &quot;stl&quot;,</span><br><span class="line">&quot;LOCAL_STRIP_MODULE&quot;:            &quot;strip&quot;,</span><br><span class="line">&quot;LOCAL_MULTILIB&quot;:                &quot;compile_multilib&quot;,</span><br><span class="line">&quot;LOCAL_ARM_MODE_HACK&quot;:           &quot;instruction_set&quot;,</span><br><span class="line">&quot;LOCAL_SDK_VERSION&quot;:             &quot;sdk_version&quot;,</span><br><span class="line">&quot;LOCAL_NDK_STL_VARIANT&quot;:         &quot;stl&quot;,</span><br><span class="line">&quot;LOCAL_JAR_MANIFEST&quot;:            &quot;manifest&quot;,</span><br><span class="line">&quot;LOCAL_JARJAR_RULES&quot;:            &quot;jarjar_rules&quot;,</span><br><span class="line">&quot;LOCAL_CERTIFICATE&quot;:             &quot;certificate&quot;,</span><br><span class="line">&quot;LOCAL_PACKAGE_NAME&quot;:            &quot;name&quot;,</span><br><span class="line">&quot;LOCAL_MODULE_RELATIVE_PATH&quot;:    &quot;relative_install_path&quot;,</span><br><span class="line">&quot;LOCAL_PROTOC_OPTIMIZE_TYPE&quot;:    &quot;proto.type&quot;,</span><br><span class="line">&quot;LOCAL_MODULE_OWNER&quot;:            &quot;owner&quot;,</span><br><span class="line">&quot;LOCAL_RENDERSCRIPT_TARGET_API&quot;: &quot;renderscript.target_api&quot;,</span><br><span class="line">&quot;LOCAL_NOTICE_FILE&quot;:             &quot;notice&quot;,</span><br><span class="line">&quot;LOCAL_JAVA_LANGUAGE_VERSION&quot;:   &quot;java_version&quot;,</span><br><span class="line">&quot;LOCAL_INSTRUMENTATION_FOR&quot;:     &quot;instrumentation_for&quot;,</span><br><span class="line">&quot;LOCAL_MANIFEST_FILE&quot;:           &quot;manifest&quot;,</span><br><span class="line"> </span><br><span class="line">&quot;LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING&quot;: &quot;dex_preopt.profile&quot;,</span><br></pre></td></tr></table></figure><hr><h2 id="将HIDL接口服务从设备system移到vendor"><a href="#将HIDL接口服务从设备system移到vendor" class="headerlink" title="将HIDL接口服务从设备system移到vendor"></a>将HIDL接口服务从设备system移到vendor</h2><ol><li>修改Android.bp中添加<code>vendor:true</code>（或者<code>vendor_avaiable：ture</code>）；如果是Android.mk，则添加如下：</li></ol><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">LOCAL_MODULE_PATH_64 &#x3D; $(TARGET_OUT_VENDOR)&#x2F;lib64</span><br><span class="line">LOCAL_MODULE_PATH_32 &#x3D; $(TARGET_OUT_VENDOR)&#x2F;lib32</span><br></pre></td></tr></table></figure><ol start="2"><li>如果有<code>.rc</code>文件，则需要修改其中的目录；其中<code>class hal</code>是不会自动启动的，所以需要将其修改成<code>class main</code> ；并且<code>onrestart restart</code>后面添加的是service名称。</li></ol><p>可以在<code>adb shell；cd /vendor/bin/</code>里面手动执行bin文件，然后查看进程是否启动。</p><ol start="3"><li>在seploicy添加权限：<code>device/PLATFORM/sepolicy/basic/non_plat/file_contexts</code></li></ol><hr><h3 id="Android-bp（可参考）"><a href="#Android-bp（可参考）" class="headerlink" title="Android.bp（可参考）"></a>Android.bp（可参考）</h3><blockquote><p><a href="https://source.android.google.cn/devices/architecture/vndk/build-system" target="_blank" rel="noopener">VNDK 构建系统支持</a></p></blockquote><p>系统将通过以下规则确定cc_library或cc_library_shared的默认安装路径：</p><p>将核心变体安装到/system/lib[64]中</p><p>供应商变体安装路径可能会有所不同：</p><ul><li><p>如果<code>vndk.enabled</code>为false，则将供应商变体将安装到<code>/vendor/lib[64]</code>中</p></li><li><p>如果<code>vndk.enabled</code>为true，则<code>vndk.support_system_process</code>可以是true或false。如果：</p></li><li><ul><li>为false，则供应商变体将安装到<code>/system/lib[64]/vndk-${VER}</code>中</li></ul></li><li><ul><li>为true，则供应商变体将安装到<code>/system/lib[64]/vndk-sp-${VER}</code>中</li></ul></li></ul><hr><h2 id="C函数memset-…"><a href="#C函数memset-…" class="headerlink" title="C函数memset(…)"></a>C函数memset(…)</h2><blockquote><p>函数解释：将s中当前位置后面的n个字节<code>（typedef unsigned int size_t ）</code>用ch替换并返回s</p></blockquote><ul><li>memset：作用是在一段内存块中填充某个给定的值，它是对较大的结构体或数组进行清零操作的一种最快方法</li><li>memset()函数原型：是<code>extern void *memset(void *buffer, int c, int count)</code>，buffer为指针或是数组，c是赋给buffer的值，count是buffer的长度</li></ul><hr><h2 id="domain-te错误类型"><a href="#domain-te错误类型" class="headerlink" title="domain.te错误类型"></a>domain.te错误类型</h2><blockquote><p>错误日志：<code>The following types on /vendor/ must be associated with the &quot;vendor_file_type&quot; attribute: ×××_exec</code></p></blockquote><ul><li>如果是system/则<code>type ***_exec, exec_type, system_file_type, file_type;</code></li><li>如果是svendor/则<code>type ***_exec, exec_type, vendor_file_type, file_type;</code></li></ul><h2 id="查看手机硬件设备（包含屏幕信息）"><a href="#查看手机硬件设备（包含屏幕信息）" class="headerlink" title="查看手机硬件设备（包含屏幕信息）"></a>查看手机硬件设备（包含屏幕信息）</h2><ul><li><code>adb shell cat proc/cmdline</code></li></ul><h2 id="ubuntu的ll命令不能使用"><a href="#ubuntu的ll命令不能使用" class="headerlink" title="ubuntu的ll命令不能使用"></a>ubuntu的<code>ll</code>命令不能使用</h2><p>将<code>ll</code>作为一个别名：</p><figure class="highlight plain"><figcaption><span>.bash_profile</span></figcaption><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">+ alias ll&#x3D;&#39;ls -alF&#39;&#39;</span><br></pre></td></tr></table></figure><p>然后执行<code>source .bash_profile</code>生效</p><h2 id="ubuntu挂载硬盘"><a href="#ubuntu挂载硬盘" class="headerlink" title="ubuntu挂载硬盘"></a>ubuntu挂载硬盘</h2><ol><li>查看硬盘：<code>sudo fdisk -l</code></li></ol><p>比如打印：<code>Disk /dev/sdb: 3000.6 GB, 3000592982016 bytes</code></p><ol start="2"><li>编辑添加到文件：<code>sudo vim /etc/fstab</code></li></ol><p>比如打印：<code>/dev/sdb /sunwg/ ext4 defaults 0 0 (后面的目录是挂载的目录)</code></p><ol start="3"><li>重启电脑，然后<code>df -h</code>查看系统分区</li></ol><h2 id="find命令"><a href="#find命令" class="headerlink" title="find命令"></a>find命令</h2><p><code>find ./ -iname &quot;*gpuinfo*&quot;</code></p><hr><p>##　查看CPU/GPU硬件频率</p><ul><li>查看CPU: <code>adb shell &quot;cat /proc/cpufreq/*/cpufreq_freq&quot;</code></li><li>查看DDR: <code>adb shell &quot;cat /sys/devices/platform/10012000.dvfsrc/helio-dvfsrc/dvfsrc_dump | grep -e uv -e khz -e CONTROL -e VCORE_OPP -e DDR_OPP -e LEVEL -e RSV_9&quot;</code></li><li>查看GPU频率： <code>adb shell cat /proc/gpufreq/gpufreq_var_dump</code>或者<code>adb shell cat /proc/gpufreq/gpufreq_opp_dump</code></li></ul><h3 id="设置GPU频率"><a href="#设置GPU频率" class="headerlink" title="设置GPU频率"></a>设置GPU频率</h3><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 root;</span><br><span class="line">adb shell &quot;echo 1000000 &gt; &#x2F;proc&#x2F;gpufreq&#x2F;gpufreq_opp_freq&quot;</span><br></pre></td></tr></table></figure><hr><h2 id="☆CPU-GPU信息文件路径"><a href="#☆CPU-GPU信息文件路径" class="headerlink" title="☆CPU/GPU信息文件路径"></a>☆CPU/GPU信息文件路径</h2><p>CPU信息是在：<code>/sys/devices/system/cpu</code><br>GPU的信息在如下目录查看：<code>/sys/class/kgsl/kgsl-3d0</code></p><hr><h2 id="加大dequeuebuffer数目"><a href="#加大dequeuebuffer数目" class="headerlink" title="加大dequeuebuffer数目"></a>加大dequeuebuffer数目</h2><p>有时候从systrace中看到dequeuebuffer耗时长，如果可能和gpu/cpu相关，则可以参照上面的方式对gpu/cpu进行提频；</p><p>也可以尝试修改文件：<code>/frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp</code></p><p><code>mProducer-&gt;setMaxDequeuedBufferCount(1); //增加这里值</code></p><p>比如修改为3，看是否有改善</p><hr><h2 id="windows-cmd过滤log"><a href="#windows-cmd过滤log" class="headerlink" title="windows cmd过滤log"></a>windows cmd过滤log</h2><p><code>adb logcat |find &quot;***&quot;</code></p><h2 id="vscode插件"><a href="#vscode插件" class="headerlink" title="vscode插件"></a>vscode插件</h2><p>gitlens：可以查看提交</p><hr><h2 id="抓取perfetto-trace（替代systrace）"><a href="#抓取perfetto-trace（替代systrace）" class="headerlink" title="抓取perfetto-trace（替代systrace）"></a>抓取perfetto-trace（替代systrace）</h2><ol><li>打开<code>开发者选项-&gt;系统跟踪-&gt;显示‘快捷设置’图块</code><br>或者命令方式打开：<code>adb shell am start com.android.traceur/com.android.traceur.MainActivity</code></li><li>然后点击“录制跟踪记录”开始抓取</li><li>点击停止后分享生成文件，或者命令导出：<code>adb pull data/local/traces</code></li><li>使用chrome登录perfetto-trace网站：<code>https://ui.perfetto.dev/#!/</code></li></ol><p>或者转换成html格式：</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></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; 仅支持python 2.7 且需要GLIBC_2.28版本</span><br><span class="line">curl https:&#x2F;&#x2F;get.perfetto.dev&#x2F;traceconv -o traceconv</span><br><span class="line"></span><br><span class="line">chmod +x traceconv</span><br><span class="line"></span><br><span class="line">.&#x2F;traceconv [text|json|systrace|profile] [input proto file] [output file]</span><br></pre></td></tr></table></figure><h3 id="systrace打开空白或者打不开"><a href="#systrace打开空白或者打不开" class="headerlink" title="systrace打开空白或者打不开"></a>systrace打开空白或者打不开</h3><p><strong>在chrome地址栏中输入”chrome:tracing”，然后点击load按钮load你的trace.html文件</strong></p><h3 id="atrace转换到html"><a href="#atrace转换到html" class="headerlink" title="atrace转换到html"></a>atrace转换到html</h3><p><code>./systrace.py --from-file 2-1.atrace</code></p><p>结果文件生成2-1.html</p><h3 id="systrace的类别和选项（直接添加）"><a href="#systrace的类别和选项（直接添加）" class="headerlink" title="systrace的类别和选项（直接添加）"></a>systrace的类别和选项（直接添加）</h3><blockquote><p>systrace脚本来源：<code>Android\Sdk\platform-tools\systrace</code></p><p>示例命令：<code>./systrace.py -b 8000 -t 5 –list-categories=&quot;Screen turning on&quot; hal app sched gfx view wm power binder_driver -o systrace.html</code></p></blockquote><table><thead><tr><th align="center">options</th><th align="center">描述</th></tr></thead><tbody><tr><td align="center">-o &lt; FILE &gt;</td><td align="center">输出的目标文件</td></tr><tr><td align="center">-t N, –time=N</td><td align="center">执行时间，默认5s</td></tr><tr><td align="center">-b N, –buf-size=N</td><td align="center">buffer大小（单位kB),用于限制trace总大小，默认无上限</td></tr><tr><td align="center">-k &lt; KFUNCS &gt;，–ktrace=&lt; KFUNCS &gt;</td><td align="center">追踪kernel函数，用逗号分隔</td></tr><tr><td align="center">-a &lt; APP_NAME &gt;,–app=&lt; APP_NAME &gt;</td><td align="center">追踪应用包名，用逗号分隔</td></tr><tr><td align="center">–from-file=&lt; FROM_FILE &gt;</td><td align="center">从文件中创建互动的systrace</td></tr><tr><td align="center">-e &lt; DEVICE_SERIAL &gt;,–serial=&lt; DEVICE_SERIAL &gt;</td><td align="center">指定设备</td></tr><tr><td align="center">-l, –list-categories</td><td align="center">列举可用的tags</td></tr></tbody></table><table><thead><tr><th align="center">category</th><th align="center">描述</th></tr></thead><tbody><tr><td align="center">gfx</td><td align="center">Graphics</td></tr><tr><td align="center">input</td><td align="center">Input</td></tr><tr><td align="center">view</td><td align="center">View System</td></tr><tr><td align="center">webview</td><td align="center">WebView</td></tr><tr><td align="center">wm</td><td align="center">Window Manager</td></tr><tr><td align="center">am</td><td align="center">Activity Manager</td></tr><tr><td align="center">sm</td><td align="center">Sync Manager</td></tr><tr><td align="center">audio</td><td align="center">Audio</td></tr><tr><td align="center">video</td><td align="center">Video</td></tr><tr><td align="center">camera</td><td align="center">Camera</td></tr><tr><td align="center">hal</td><td align="center">Hardware Modules</td></tr><tr><td align="center">app</td><td align="center">Application</td></tr><tr><td align="center">res</td><td align="center">Resource Loading</td></tr><tr><td align="center">dalvik</td><td align="center">Dalvik VM</td></tr><tr><td align="center">rs</td><td align="center">RenderScript</td></tr><tr><td align="center">bionic</td><td align="center">Bionic C Library</td></tr><tr><td align="center">power</td><td align="center">Power Management</td></tr><tr><td align="center">sched</td><td align="center">CPU Scheduling</td></tr><tr><td align="center">irq</td><td align="center">IRQ    Events</td></tr><tr><td align="center">freq</td><td align="center">CPU Frequency</td></tr><tr><td align="center">idle</td><td align="center">CPU Idle</td></tr><tr><td align="center">disk</td><td align="center">Disk I/O</td></tr><tr><td align="center">mmc</td><td align="center">eMMC commands</td></tr><tr><td align="center">load</td><td align="center">CPU Load</td></tr><tr><td align="center">sync</td><td align="center">Synchronization</td></tr><tr><td align="center">workq</td><td align="center">Kernel Workqueueszuidi</td></tr></tbody></table><p>几个比较常用的模块：</p><ul><li>sched：CPU调度的信息，非常重要；你能看到CPU在每个时间段在运行什么线程；线程调度情况，比如锁信息</li><li>gfx：Graphic系统的相关信息，包括SurfaceFlinger，VSYNC消息，Texture，RenderThread等；分析卡顿非常依赖这个</li><li>view：View绘制系统的相关信息，比如onMeasure，onLayout等；对分析卡顿比较有帮助</li><li>am：ActivityManager调用的相关信息；用来分析Activity的启动过程比较有效</li><li>dalvik： 虚拟机相关信息，比如GC停顿等</li><li>binder_driver：Binder驱动的相关信息，如果你怀疑是Binder IPC的问题，不妨打开这个</li><li>core_services：SystemServer中系统核心Service的相关信息，分析特定问题用</li></ul><hr><h2 id="SElinux权限相关"><a href="#SElinux权限相关" class="headerlink" title="SElinux权限相关"></a>SElinux权限相关</h2><blockquote><p>参考网站<a href="https://www.pianshen.com/article/6549296922/" target="_blank" rel="noopener">程序员大本营selinux</a></p></blockquote><ul><li>scontext表示进程的<code>SContext，u:r:bluetooth:s0</code>，属于bluetooth域；</li><li>tcontext表示目标的<code>SContext，u:r:system_data_file:s0</code>，属于system_data_file类型;</li><li>tclass表示进程要操作的<code>ObjectClass</code>，dir表示目录；</li></ul><h3 id="自动生成selinux权限代码"><a href="#自动生成selinux权限代码" class="headerlink" title="自动生成selinux权限代码"></a>自动生成selinux权限代码</h3><ol><li><p>过滤<code>avc</code>关键词代码，导入到新文件log.txt</p></li><li><p>需要安装：<code>policycoreutils</code></p></li><li><p>执行命令或区域权限的代码：<br><code>cat log.txt  | grep avc | audit2al</code></p></li><li><p>命令执行后生成权限相关代码，然后进行修改，例如下面：</p></li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">#&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D; mcd &#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;</span><br><span class="line">allow mcd sysfs_battery_supply:dir search;</span><br><span class="line">allow mcd sysfs_battery_supply:file &#123; read open &#125;;</span><br><span class="line">allow mcd sysfs_batteryinfo:dir search;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h3 id="开启-关闭Selinux权限"><a href="#开启-关闭Selinux权限" class="headerlink" title="开启/关闭Selinux权限"></a>开启/关闭Selinux权限</h3><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 setenforce 0 &#x2F;&#x2F;设置成permissive模式，临时关闭selinux</span><br><span class="line">adb shell setenforce 1 &#x2F;&#x2F;设置成enforce模式</span><br></pre></td></tr></table></figure><p>重启后失效</p><p>使用<code>adb shell getenforce</code>查看当前权限状态。</p><p>可能返回结果有三种：</p><ul><li>Disabled代表SELinux被禁用</li><li>Permissive代表仅记录安全警告但不阻止可疑行</li><li>Enforcing代表记录警告且阻止可疑行为</li></ul><hr><h2 id="进入fastboot"><a href="#进入fastboot" class="headerlink" title="进入fastboot"></a>进入fastboot</h2><p><code>adb reboot fastboot</code></p><p>或者</p><p><code>adb reboot bootloader</code></p><hr><h2 id="LCD屏幕低温残影现象"><a href="#LCD屏幕低温残影现象" class="headerlink" title="LCD屏幕低温残影现象"></a>LCD屏幕低温残影现象</h2><p>LCD屏幕里的像素发光是由液晶偏转来控制的，而液晶这种材料对温度十分敏感，温度越低，液晶翻转的速度也就越慢，屏幕的响应速度也就会越慢。当画面已经刷新到了下一帧时，液晶还未翻转完成，新生成的画面与未消失的画面错位存在，看上去就像重影一样。</p><p>而OLED屏幕的像素点是由一个个单独控制的发光二级管所组成的，低温对其响应速度影响不大，所以单独发光的OLED的响应速度要远高于LCD屏幕。所以OLED屏幕的手机在低温下几乎不会出现拖影的问题。</p><p>此外，录屏是录取不到拖影的现象，需要拍视频才能看的到。</p><hr><h2 id="makefile"><a href="#makefile" class="headerlink" title="makefile"></a>makefile</h2><p>makefile中<code>-D*</code>表示：<code>#define *</code></p><p>如：<code>-DPOSGP730 等价于 #define POSGP730</code></p><hr><h2 id="亮度单位nit"><a href="#亮度单位nit" class="headerlink" title="亮度单位nit"></a>亮度单位nit</h2><p><code>nit(尼特),1nit=1坎德拉/平方米[cd/m2]</code></p><p>亮度(L)指物体明暗的程度,定义是单位面积的发光强度,是表示眼睛从某一方向所看到物体反射光的强度。</p><h3 id="光学单位lux"><a href="#光学单位lux" class="headerlink" title="光学单位lux"></a>光学单位lux</h3><p>0.1Lux是指摄像机的照度</p><p>Lux是一个光学单位，指的是每平方米的光照亮，可以理解成对光线强度的反应，在同等光线条件下，LUX越小的摄像机感光度越好。</p><table><thead><tr><th align="center">场所/环境</th><th align="center">光照度</th></tr></thead><tbody><tr><td align="center">晴天</td><td align="center">30000～300000 lux</td></tr><tr><td align="center">晴天室内</td><td align="center">100～1000 lux</td></tr><tr><td align="center">阴天</td><td align="center">3000～10000 lux</td></tr><tr><td align="center">阴暗夜晚</td><td align="center">0.003～0.0007 lux</td></tr><tr><td align="center">夜间路灯</td><td align="center">0.1 lux</td></tr></tbody></table><hr><h2 id="启动activity-关闭activity"><a href="#启动activity-关闭activity" class="headerlink" title="启动activity/关闭activity"></a>启动activity/关闭activity</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;使用Action方式打开系统设置-输入法设置</span><br><span class="line">adb shell am start -a android.settings.INPUT_METHOD_SETTINGS</span><br><span class="line"> </span><br><span class="line">&#x2F;&#x2F;使用组件名方式启动照相机功能</span><br><span class="line">adb shell am start -n com.android.camera&#x2F;.Camera</span><br><span class="line"> </span><br><span class="line">&#x2F;&#x2F;打开拨号界面，并传递一个DATA_URI数据给拨号界面</span><br><span class="line">am start -a android.intent.action.CALL -d tel:10086</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;使用ComponentName方式启动一个Service</span><br><span class="line">adb shell am startservice com.some.package.name&#x2F;.YourServiceSubClassName</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;关闭指定包名的应用程序 </span><br><span class="line">adb shell am force-stop com.some.package</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;杀死进程</span><br><span class="line">adb shell am kill com.some.package</span><br></pre></td></tr></table></figure><hr><h2 id="Git命令"><a href="#Git命令" class="headerlink" title="Git命令"></a>Git命令</h2><h3 id="查看代码提交历史（具体到某一行）"><a href="#查看代码提交历史（具体到某一行）" class="headerlink" title="查看代码提交历史（具体到某一行）"></a>查看代码提交历史（具体到某一行）</h3><p><code>git blame SurfaceFlinger.cpp（文件名）</code></p><h3 id="提交代码"><a href="#提交代码" class="headerlink" title="提交代码"></a>提交代码</h3><p><code>git push origin HEAD:refs/for/×××Branch</code></p><h3 id="提交草稿代码"><a href="#提交草稿代码" class="headerlink" title="提交草稿代码"></a>提交草稿代码</h3><p><code>git push origin HEAD:refs/drafts/×××Branch</code></p><h2 id="提交代码、sync代码报错Agent-admitted-failure-to-sign-using-the-key"><a href="#提交代码、sync代码报错Agent-admitted-failure-to-sign-using-the-key" class="headerlink" title="提交代码、sync代码报错Agent admitted failure to sign using the key"></a>提交代码、sync代码报错<code>Agent admitted failure to sign using the key</code></h2><p>执行：<code>ssh-add ~/.ssh/id_rsa</code></p><hr><h2 id="apt-get报错不能进行正常更新"><a href="#apt-get报错不能进行正常更新" class="headerlink" title="apt-get报错不能进行正常更新"></a>apt-get报错不能进行正常更新</h2><blockquote><p>提示错误信息<code>The package lists or status file could not be parsed or opened</code></p></blockquote><p>解决方法：</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">sudo rm -vf &#x2F;var&#x2F;lib&#x2F;apt&#x2F;lists&#x2F;*</span><br><span class="line">sudo apt-get update</span><br></pre></td></tr></table></figure><h2 id="ubuntu自带截图快捷键"><a href="#ubuntu自带截图快捷键" class="headerlink" title="ubuntu自带截图快捷键"></a>ubuntu自带截图快捷键</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Alt + Print Screen #截取选中的窗口</span><br><span class="line"></span><br><span class="line">Shift + Print Screen #自由选区</span><br><span class="line"></span><br><span class="line">Ctrl + Print Screen  #整个桌面</span><br><span class="line"></span><br><span class="line">Ctrl + Alt + Print Screen #选中的窗口</span><br><span class="line"></span><br><span class="line">Shift + Ctrl + Print Screen #自由选区</span><br></pre></td></tr></table></figure><h2 id="gedit乱码"><a href="#gedit乱码" class="headerlink" title="gedit乱码"></a>gedit乱码</h2><p>执行：<code>gconftool-2 --set --type=list --list-type=string /apps/gedit-2/preferences/encodings/auto_detected &quot;[UTF-8,CURRENT,GB18030,BIG5-HKSCS,UTF-16]&quot;</code></p><h2 id="跳过开机向导"><a href="#跳过开机向导" class="headerlink" title="跳过开机向导"></a>跳过开机向导</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">adb shell settings put secure user_setup_complete 1</span><br><span class="line">adb shell settings put global device_provisioned 1</span><br></pre></td></tr></table></figure><h2 id="ubuntu快速下载命令"><a href="#ubuntu快速下载命令" class="headerlink" title="ubuntu快速下载命令"></a>ubuntu快速下载命令</h2><p><code>axel -n 20 下载链接</code></p><hr><h2 id="谷歌GMS服务"><a href="#谷歌GMS服务" class="headerlink" title="谷歌GMS服务"></a>谷歌GMS服务</h2><blockquote><p>GMS（Google Mobile Service），即谷歌移动服务，是Google提供的移动设备上的一系列应用服务，比如包含Play Sotre，Gmail，YouTube，Chrome，Google Maps等。<br>如果需要预置GMS应用，需要通过Google认证，比如CTS、GTS等跑测。</p></blockquote><h3 id="GMS测试注意事项"><a href="#GMS测试注意事项" class="headerlink" title="GMS测试注意事项"></a>GMS测试注意事项</h3><ol><li>如果升级应用到最新版本不再复现，则是应用本身的问题，Google在升级过程中已进行修补</li><li>如果仍旧复现，则使用对比机器进行对比验证</li></ol><h3 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h3><h4 id="开机向导设置密码后关机再开机黑屏"><a href="#开机向导设置密码后关机再开机黑屏" class="headerlink" title="开机向导设置密码后关机再开机黑屏"></a>开机向导设置密码后关机再开机黑屏</h4><p>修改方式： </p><ol><li>找到<code>frameworks/base/packages/SystemUI/src/com/android/systemui/Keyguard/KeyguardViewMediator.java</code></li><li>找到函数<code>private void doKeyguardLocked(Bundle options)</code></li><li>修改<code>if (!lockedOrMissing &amp;&amp; !provisioned &amp;&amp; !antiTheftLocked)</code> —&gt; <code>if(!lockedOrMissing &amp;&amp; shouldWaitForProvisioning() &amp;&amp; !antiTheftLocked)</code></li></ol><h4 id="Play-Store无法正常连接"><a href="#Play-Store无法正常连接" class="headerlink" title="Play Store无法正常连接"></a>Play Store无法正常连接</h4><blockquote><p>无法登陆Google账户，提示与Google服务器通信时出现问题</p></blockquote><p>如果可以登录Google账户（设置添加Google账户），而不能进入Google Play，可以抓取一份log，搜索关键字<code>CheckinTask</code>，如果出现如下log：</p><p><code>E CheckinTask: Checkin failed: https://android.clients.google.com/checkin (request #0): java.io.IOException: Bad Content-Type: text/html; charset=UTF-8</code></p><p>就先检查系统以下属性值的设定是否存在“非法”字符：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">ro.product.model</span><br><span class="line">ro.product.name</span><br><span class="line">ro.product.device</span><br><span class="line">ro.product.board</span><br></pre></td></tr></table></figure><h4 id="Play-Store下载应用失败"><a href="#Play-Store下载应用失败" class="headerlink" title="Play Store下载应用失败"></a>Play Store下载应用失败</h4><blockquote><p>从服务器检索信息时出错 (Error retrieving information from server)</p></blockquote><blockquote><p>可以参考：<a href="https://www.uso.cn/post/view/47110" target="_blank" rel="noopener">Google Play商店的各种报错解释以及修复方法</a></p></blockquote><h4 id="GMS应用首次登陆闪退，第二次正常"><a href="#GMS应用首次登陆闪退，第二次正常" class="headerlink" title="GMS应用首次登陆闪退，第二次正常"></a>GMS应用首次登陆闪退，第二次正常</h4><blockquote><p>闪退问题先Check log中是否收到<code>PACKAGE_CHANGED的</code>广播</p></blockquote><blockquote><p>查看<code>system log</code>，Google Maps如果有发生package changed，这是厂商的Maps版本比较旧，开机后联网情况下会自动从server升级造成<code>process kill</code>，属于正常的行为。关键log如下：</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">V ActivityManager: Broadcast: Intent &#123; act&#x3D;android.intent.action.PACKAGE_CHANGED dat&#x3D;package:com.google.android.apps.maps flg&#x3D;0x4000010 (has extras) &#125; ordered&#x3D;false userid&#x3D;0 callerApp&#x3D;null</span><br><span class="line">I ActivityManager: Force stopping com.google.android.apps.maps appid&#x3D;10060 user&#x3D;0: pkg changed</span><br><span class="line">I ActivityManager: Killing 6004:com.google.android.apps.maps&#x2F;u0a60 (adj 0): stop com.google.android.apps.maps</span><br></pre></td></tr></table></figure><h4 id="GMS应用缺少部分菜单"><a href="#GMS应用缺少部分菜单" class="headerlink" title="GMS应用缺少部分菜单"></a>GMS应用缺少部分菜单</h4><p>如果系统分区太小，会拿掉一些feature，你可以尝试更大的RAM size，比如1GB或更大。</p><h4 id="GMS部分应用联网后消失"><a href="#GMS部分应用联网后消失" class="headerlink" title="GMS部分应用联网后消失"></a>GMS部分应用联网后消失</h4><p>GMS中部分应用有地域使用限制，Play Books, Play Magazine, Play Movies等应用目前不支持在中国大陆这边使用，所以联网后应用会自己屏蔽掉，此为正常现象。</p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;android display/graphics相关的调试方法和优化工作效率技巧整理&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>数据结构之二叉树</title>
    <link href="https://alonealive.github.io/Blog/2021/05/28/2021/210528_cpp_DataStructure4/"/>
    <id>https://alonealive.github.io/Blog/2021/05/28/2021/210528_cpp_DataStructure4/</id>
    <published>2021-05-28T14:05:00.000Z</published>
    <updated>2021-05-28T12:16:51.908Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>包含树、二叉树、哈希表、二分查找的定义和实现</p></blockquote><a id="more"></a><h2 id="树"><a href="#树" class="headerlink" title="树"></a>树</h2><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><p>树（Tree）是 n（n≥0）个结点的有限集 T，当 T 为空时称为空树，否则它满足以下两个条件：</p><ol><li>有且仅有一个特定的数据元素称为根（root）的结点，根结点没有前驱结点</li><li>其余的结点可分为m（m＞0）个互不相交的子集Tl, T2,…, Tm，其中每个集合Ti（1≤i≤m）本身又是一棵树，并称其为根的子树（subtree）</li></ol><p>树的递归定义刻画了树的固有特性：一棵非空树是由若干棵子树构成的，而子树又可由若干棵更小的子树构成。因此，树结构的很多算法都使用了递归方法</p><h3 id="基本术语"><a href="#基本术语" class="headerlink" title="基本术语"></a>基本术语</h3><ol><li>结点的度（degree）：结点拥有的子树数</li><li>叶子（终端结点）：度为0的结点</li><li>非终端结点：度不为0的结点</li><li>结点的层次：树中根结点的层次为1，其子树的根为第2层，其余类推</li><li>树的度：树中所有结点的最大度数</li><li>树的深度：树中所有结点层次的最大值</li><li>有序树、无序树：如果树中每棵子树从左向右的排列拥有一定的顺序，不得互换，则称为有序树；否则称为无序树</li><li><strong>森林：m（m≥0）棵互不相交的树的集合</strong></li></ol><p>在树结构中，结点之间的关系又可用家族关系描述，定义如下：</p><ul><li>孩子、双亲：结点子树的根称为这个结点的孩子，而这个结点又被称为孩子的双亲（父结点、子结点）</li><li>子孙：以某结点为根的子树中的所有结点都被称为是该结点的子孙</li><li>祖先：从根结点到该结点路径上的所有结点</li><li>兄弟：同一个双亲的孩子之间互为兄弟</li><li>堂兄弟：双亲在同一层的结点互为堂兄弟</li></ul><h3 id="树的遍历"><a href="#树的遍历" class="headerlink" title="树的遍历"></a>树的遍历</h3><blockquote><p>之所以叫前序、中序、后序遍历，是因为根节点在前、中、后</p></blockquote><blockquote><p>前序：根左右；中序：左根右；后序：左右根；</p></blockquote><p>中序常用来在二叉搜索数中得到递增的有序序列；</p><p>后序可用于数学中的后缀表示法，结合栈处理表达式，每遇到一个操作符，就可以从栈中弹出栈顶的两个元素，计算并将结果返回到栈中；</p><h4 id="先序遍历"><a href="#先序遍历" class="headerlink" title="先序遍历"></a>先序遍历</h4><p>首先访问根节点，然后遍历左子树，最后遍历右子树</p><p><img src="tree_order1.png" alt="先序遍历"></p><h4 id="中序遍历"><a href="#中序遍历" class="headerlink" title="中序遍历"></a>中序遍历</h4><p>先遍历左子树，然后<strong>访问根节点</strong>，然后遍历右子树。</p><p><img src="tree_order1.png" alt="中序遍历"></p><h4 id="后序遍历"><a href="#后序遍历" class="headerlink" title="后序遍历"></a>后序遍历</h4><p>先遍历左子树，然后遍历右子树，最后访问树的根节点</p><p><img src="tree_order1.png" alt="后序遍历"></p><hr><h3 id="森林遍历"><a href="#森林遍历" class="headerlink" title="森林遍历"></a>森林遍历</h3><p>根据森林和树相互递归的定义，可推出森林的两种遍历方法：</p><p><strong>先序遍历森林：</strong></p><ol><li>访问森林中第一棵树的根结点；</li><li>先序遍历第一棵树中根结点的子树森林；</li><li>先序遍历除去第一棵树之后剩余的树构成的森林</li></ol><p><strong>后序遍历森林：</strong></p><ol><li>后序遍历森林中第一棵树的根结点的子树森林；</li><li>访问第一棵树的根结点；</li><li>后序遍历除去第一棵树之后剩余的树构成的森林。</li></ol><p><img src="forest1.png" alt="森林"></p><p>上面的森林进行先序遍历和后序遍历，分别得到森林的先序序列为<code>1 23 4 5 6 7 8 9 10 11 12</code>，后序序列为<code>5 2 3 4 1 8 9 7 6 12 11 10</code></p><hr><h2 id="二叉树"><a href="#二叉树" class="headerlink" title="二叉树"></a>二叉树</h2><h3 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h3><p>一棵二叉树（binary tree）是结点的一个有限集合，该集合或者为空，或者由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。</p><p>五种基本形态：</p><p><img src="binaryTree1.png" alt="二叉树的五种基本形态"></p><h3 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h3><ol><li>在二叉树的第i层上至多有$2^{i-1}$个结点（i≥1）</li><li>深度为k的二叉树至多有$2^{k-1}$个结点（k≥1）</li><li>对任意一棵非空二叉树T，若其叶结点数为$n_0$，度为2的结点数为$n_2$，则$n_0=n_2+1$（根节点加上其他度为2的结点树）</li><li>具有n个结点的完全二叉树的深度为$[log_2n]+1$</li></ol><p>证明：设完全二叉树的深度为k，由它的定义和性质可知2k-1-1＜n≤2k-1 或2k-l≤n＜2k，取对数后有k-1≤log2n＜k，因为k是整数，所以k = log2n || +1（或者）</p><ol start="5"><li>如果将一棵有 n个结点的完全二叉树（其深度为log n||2+）自顶向下、同一1层自左向右编号为1, 2, 3,…, n，则对任意一个结点i（1≤i≤n）有：① 如果i = 1，则此结点为二叉树的根，无双亲；如果 i＞1，则其双亲结点是floor(i/2)。② 如果2i＞n，则结点 i无左孩子（结点 i 为叶子结点）；否则其左孩子是结点2i。③ 如果2i+1＞n，则结点 i 无右孩子；否则其右孩子是结点2i+1</li></ol><h4 id="满二叉树和完全二叉树"><a href="#满二叉树和完全二叉树" class="headerlink" title="满二叉树和完全二叉树"></a>满二叉树和完全二叉树</h4><ul><li><p>满二叉树（full binarytree）是指深度为k，且有2k-1个结点的二叉树。特点是，每一层上结点数都是最大结点数，即不存在度为1的结点</p></li><li><p>完全二叉树（completed binary tree）是指深度为k，有n个结点的二叉树，<strong>除最后一层外，其余层均是满的，且最下面一层的结点都集中在最左边的位置上</strong></p></li></ul><p><img src="tree2.png" alt="二叉树"></p><hr><h3 id="层序遍历"><a href="#层序遍历" class="headerlink" title="层序遍历"></a>层序遍历</h3><p>层序遍历就是逐层遍历树结构。</p><p>广度优先搜索是一种广泛运用在树或图这类数据结构中，遍历或搜索的算法。</p><p>该算法从一个根节点开始，首先访问节点本身。 然后遍历它的相邻节点，其次遍历它的二级邻节点、三级邻节点，以此类推。</p><p>当我们在树中进行广度优先搜索时，我们访问的节点的顺序是按照层序遍历顺序的。</p><h4 id="二叉树的层序遍历实现"><a href="#二叉树的层序遍历实现" class="headerlink" title="二叉树的层序遍历实现"></a>二叉树的层序遍历实现</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * struct TreeNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     TreeNode *left;</span></span><br><span class="line"><span class="comment"> *     TreeNode *right;</span></span><br><span class="line"><span class="comment"> *     TreeNode() : val(0), left(nullptr), right(nullptr) &#123;&#125;</span></span><br><span class="line"><span class="comment"> *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) &#123;&#125;</span></span><br><span class="line"><span class="comment"> *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) &#123;&#125;</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="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&gt; <span class="title">levelOrder</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//创建返回数组</span></span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&gt; ans;</span><br><span class="line">        <span class="comment">//创建队列</span></span><br><span class="line">        <span class="built_in">queue</span>&lt;TreeNode*&gt; que;</span><br><span class="line">        <span class="comment">//若空</span></span><br><span class="line">        <span class="keyword">if</span>(root == <span class="literal">nullptr</span>) <span class="keyword">return</span> ans;</span><br><span class="line">        <span class="comment">//根节点压入队列</span></span><br><span class="line">        que.push(root);</span><br><span class="line">        <span class="comment">//若队列不为空，循环</span></span><br><span class="line">        <span class="keyword">while</span>(!que.empty())&#123;</span><br><span class="line">            <span class="comment">//当前队列长度</span></span><br><span class="line">            <span class="keyword">int</span> queLen = que.size();</span><br><span class="line">            ans.push_back(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;());</span><br><span class="line">            <span class="comment">//循环遍历当前层所有节点</span></span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; queLen; ++i)&#123;</span><br><span class="line">                <span class="comment">//选择节点</span></span><br><span class="line">                <span class="keyword">auto</span> node = que.front();</span><br><span class="line">                <span class="comment">//选择的当前节点弹出队列</span></span><br><span class="line">                que.pop();</span><br><span class="line">                <span class="comment">//将选择的当前节点的值压入数组</span></span><br><span class="line">                ans.back().push_back(node-&gt;val);</span><br><span class="line">                <span class="comment">//寻找当前节点的下一层节点压入队列</span></span><br><span class="line">                <span class="keyword">if</span>(node-&gt;left) que.push(node-&gt;left);</span><br><span class="line">                <span class="keyword">if</span>(node-&gt;right) que.push(node-&gt;right);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><hr><h3 id="二叉搜索树"><a href="#二叉搜索树" class="headerlink" title="二叉搜索树"></a>二叉搜索树</h3><p>二叉搜索树（BST）是二叉树的一种特殊表示形式，它满足如下特性：</p><ul><li>每个节点中的值必须大于（或等于）存储在其左侧子树中的任何值</li><li>每个节点中的值必须小于（或等于）存储在其右子树中的任何值</li></ul><hr><h2 id="哈希表"><a href="#哈希表" class="headerlink" title="哈希表"></a>哈希表</h2><p>哈希表是一种数据结构，它使用哈希函数组织数据，以支持<strong>快速插入和搜索</strong></p><ul><li>哈希函数：将结点的关键字与它的存储位置之间建立一种对应关系的函数称为哈希函数。</li><li>哈希表：根据设定的哈希函数h(key)和处理冲突的方法将一组关键字映射到一个有限的连续地址区间上，这样的表称为哈希表，<strong>这一映射过程称为哈希造表或散列，所得的存储位置称为哈希地址或散列地址</strong></li><li>冲突（collision）：不同的关键字映射到同一哈希地址的现象称为冲突</li><li>同义词（synonym）：在一个哈希函数中，具有相同函数值的关键字，互称为同义词。</li></ul><p>有两种不同类型的哈希表：哈希集合和哈希映射。</p><ul><li>哈希集合是集合数据结构的实现之一，用于存储非重复值。</li><li>哈希映射是映射 数据结构的实现之一，用于存储(key, value)键值对。</li></ul><p>在标准模板库的帮助下，哈希表是易于使用的</p><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>哈希表的关键思想是使用哈希函数将键映射到存储桶。更确切地说，</p><ul><li>当我们插入一个新的键时，哈希函数将决定该键应该分配到哪个桶中，并将该键存储在相应的桶中；</li><li>当我们想要搜索一个键时，哈希表将使用相同的哈希函数来查找对应的桶，并只在特定的桶中进行搜索。</li></ul><p>例如我们使用$y = x ％ 5$作为哈希函数，然后完成插入和搜索策略：</p><ul><li>插入：我们通过哈希函数解析键，将它们映射到相应的桶中。例如，1987分配给桶2，而24分配给桶4</li><li>搜索：我们通过相同的哈希函数解析键，并仅在特定存储桶中搜索。如果我们搜索 1987，我们将使用相同的哈希函数将1987 映射到 2。因此我们在桶 2 中搜索，我们在那个桶中成功找到了 1987。例如，如果我们搜索23，将映射23到3，并在桶3中搜索。我们发现23不在桶3中，这意味着23不在哈希表中。</li></ul><h3 id="哈希集用法"><a href="#哈希集用法" class="headerlink" title="哈希集用法"></a>哈希集用法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unordered_set&gt;                // 0. include the library</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 1. initialize a hash set</span></span><br><span class="line">    <span class="built_in">unordered_set</span>&lt;<span class="keyword">int</span>&gt; hashset;   </span><br><span class="line">    <span class="comment">// 2. insert a new key</span></span><br><span class="line">    hashset.insert(<span class="number">3</span>);</span><br><span class="line">    hashset.insert(<span class="number">2</span>);</span><br><span class="line">    hashset.insert(<span class="number">1</span>);</span><br><span class="line">    <span class="comment">// 3. delete a key</span></span><br><span class="line">    hashset.erase(<span class="number">2</span>);</span><br><span class="line">    <span class="comment">// 4. check if the key is in the hash set</span></span><br><span class="line">    <span class="keyword">if</span> (hashset.count(<span class="number">2</span>) &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Key 2 is not in the hash set."</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 5. get the size of the hash set</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"The size of hash set is: "</span> &lt;&lt; hashset.size() &lt;&lt; <span class="built_in">endl</span>; </span><br><span class="line">    <span class="comment">// 6. iterate the hash set</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = hashset.begin(); it != hashset.end(); ++it) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; (*it) &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"are in the hash set."</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="comment">// 7. clear the hash set</span></span><br><span class="line">    hashset.clear();</span><br><span class="line">    <span class="comment">// 8. check if the hash set is empty</span></span><br><span class="line">    <span class="keyword">if</span> (hashset.empty()) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"hash set is empty now!"</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="使用哈希集查重"><a href="#使用哈希集查重" class="headerlink" title="使用哈希集查重"></a>使用哈希集查重</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Template for using hash set to find duplicates.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">findDuplicates</span><span class="params">(<span class="built_in">vector</span>&lt;Type&gt;&amp; keys)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Replace Type with actual type of your key</span></span><br><span class="line">    <span class="built_in">unordered_set</span>&lt;Type&gt; hashset;</span><br><span class="line">    <span class="keyword">for</span> (Type key : keys) &#123;</span><br><span class="line">        <span class="keyword">if</span> (hashset.count(key) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        hashset.insert(key);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="哈希映射用法"><a href="#哈希映射用法" class="headerlink" title="哈希映射用法"></a>哈希映射用法</h3><blockquote><p>哈希集合只能存储值。另一方面，哈希映射是映射的一种实现，它能够存储 (key,value) 键值对</p><p>由于能够存储更多信息，哈希映射可以帮助我们解决更复杂的问题。 例如，我们可以使用哈希映射按键聚合所有信息，并在平均为常量的时间内查找与特定键相关的信息。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unordered_map&gt;                // 0. include the library</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 1. initialize a hash map</span></span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">int</span>, <span class="keyword">int</span>&gt; hashmap;</span><br><span class="line">    <span class="comment">// 2. insert a new (key, value) pair</span></span><br><span class="line">    hashmap.insert(make_pair(<span class="number">0</span>, <span class="number">0</span>));  <span class="comment">//make_pair无需标明类型即可生成一个pair对象</span></span><br><span class="line">    hashmap.insert(make_pair(<span class="number">2</span>, <span class="number">3</span>));</span><br><span class="line">    <span class="comment">// 3. insert a new (key, value) pair or update the value of existed key</span></span><br><span class="line">    hashmap[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">    hashmap[<span class="number">1</span>] = <span class="number">2</span>;</span><br><span class="line">    <span class="comment">// 4. get the value of a specific key</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"The value of key 1 is: "</span> &lt;&lt; hashmap[<span class="number">1</span>] &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="comment">// 5. delete a key</span></span><br><span class="line">    hashmap.erase(<span class="number">2</span>);</span><br><span class="line">    <span class="comment">// 6. check if a key is in the hash map</span></span><br><span class="line">    <span class="keyword">if</span> (hashmap.count(<span class="number">2</span>) &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Key 2 is not in the hash map."</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 7. get the size of the hash map</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"the size of hash map is: "</span> &lt;&lt; hashmap.size() &lt;&lt; <span class="built_in">endl</span>; </span><br><span class="line">    <span class="comment">// 8. iterate the hash map</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> it = hashmap.begin(); it != hashmap.end(); ++it) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"("</span> &lt;&lt; it-&gt;first &lt;&lt; <span class="string">","</span> &lt;&lt; it-&gt;second &lt;&lt; <span class="string">") "</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"are in the hash map."</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="comment">// 9. clear the hash map</span></span><br><span class="line">    hashmap.clear();</span><br><span class="line">    <span class="comment">// 10. check if the hash map is empty</span></span><br><span class="line">    <span class="keyword">if</span> (hashmap.empty()) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"hash map is empty now!"</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h2><p>它描述了在有序集合中搜索特定值的过程。</p><p>二分查找中使用的术语：</p><ul><li>目标 Target —— 你要查找的值</li><li>索引 Index —— 你要查找的当前位置</li><li>左、右指示符 Left，Right —— 我们用来维持查找空间的指标</li><li>中间指示符 Mid —— 我们用来应用条件来确定我们应该向左查找还是向右查找的索引</li></ul><h3 id="三步骤"><a href="#三步骤" class="headerlink" title="三步骤"></a>三步骤</h3><p>二分查找一般由三个主要部分组成：</p><ol><li>预处理 —— 如果集合未排序，则进行排序</li><li>二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半</li><li>后处理 —— 在剩余空间中确定可行的候选者</li></ol><h3 id="二分查找代码模板"><a href="#二分查找代码模板" class="headerlink" title="二分查找代码模板"></a>二分查找代码模板</h3><h4 id="查找单个索引"><a href="#查找单个索引" class="headerlink" title="查找单个索引"></a>查找单个索引</h4><blockquote><p>二分查找的最基础和最基本的形式</p><p>用于查找可以通过访问数组中的单个索引来确定的元素或条件</p></blockquote><p>二分查找的最基础和最基本的形式。</p><p>查找条件可以在不与元素的两侧进行比较的情况下确定（或使用它周围的特定元素）。</p><p>不需要后处理，因为每一步中，你都在检查是否找到了元素。如果到达末尾，则知道未找到该元素。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums, <span class="keyword">int</span> target)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(nums.size() == <span class="number">0</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">int</span> left = <span class="number">0</span>, right = nums.size() - <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">while</span>(left &lt;= right)&#123;</span><br><span class="line">    <span class="comment">// Prevent (left + right) overflow</span></span><br><span class="line">    <span class="keyword">int</span> mid = left + (right - left) / <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">if</span>(nums[mid] == target)&#123; <span class="keyword">return</span> mid; &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(nums[mid] &lt; target) &#123; left = mid + <span class="number">1</span>; &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123; right = mid - <span class="number">1</span>; &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// End Condition: left &gt; right</span></span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="查找索引及其右邻居索引"><a href="#查找索引及其右邻居索引" class="headerlink" title="查找索引及其右邻居索引"></a>查找索引及其右邻居索引</h4><blockquote><p>用于查找需要访问数组中当前索引及其直接右邻居索引的元素或条件</p></blockquote><p>一种实现二分查找的高级方法。</p><p>查找条件需要访问元素的直接右邻居。</p><p>使用元素的右邻居来确定是否满足条件，并决定是向左还是向右。</p><p>保证查找空间在每一步中至少有2个元素。</p><p>需要进行后处理。当你剩下1个元素时，循环/递归结束。需要评估剩余元素是否符合条件。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums, <span class="keyword">int</span> target)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(nums.size() == <span class="number">0</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">int</span> left = <span class="number">0</span>, right = nums.size();</span><br><span class="line">  <span class="keyword">while</span>(left &lt; right)&#123;</span><br><span class="line">    <span class="comment">// Prevent (left + right) overflow</span></span><br><span class="line">    <span class="keyword">int</span> mid = left + (right - left) / <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">if</span>(nums[mid] == target)&#123; <span class="keyword">return</span> mid; &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(nums[mid] &lt; target) &#123; left = mid + <span class="number">1</span>; &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123; right = mid; &#125;  <span class="comment">//右邻居</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Post-processing:</span></span><br><span class="line">  <span class="comment">// End Condition: left == right</span></span><br><span class="line">  <span class="keyword">if</span>(left != nums.size() &amp;&amp; nums[left] == target) <span class="keyword">return</span> left;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="查找索引及其左右邻居索引"><a href="#查找索引及其左右邻居索引" class="headerlink" title="查找索引及其左右邻居索引"></a>查找索引及其左右邻居索引</h4><blockquote><p>用于搜索需要访问当前索引及其在数组中的直接左右邻居索引的元素或条件。</p></blockquote><p>实现二分查找的另一种方法。</p><p>搜索条件需要访问元素的直接左右邻居。</p><p>使用元素的邻居来确定它是向右还是向左。</p><p>保证查找空间在每个步骤中至少有 3 个元素。</p><p>需要进行后处理。当剩下2个元素时，循环/递归结束。需要评估其余元素是否符合条件。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums, <span class="keyword">int</span> target)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (nums.size() == <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = nums.size() - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (left + <span class="number">1</span> &lt; right)&#123;</span><br><span class="line">        <span class="comment">// Prevent (left + right) overflow</span></span><br><span class="line">        <span class="keyword">int</span> mid = left + (right - left) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span> (nums[mid] == target) &#123;</span><br><span class="line">            <span class="keyword">return</span> mid;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &lt; target) &#123;</span><br><span class="line">            left = mid;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            right = mid;</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">// Post-processing:</span></span><br><span class="line">    <span class="comment">// End Condition: left + 1 == right</span></span><br><span class="line">    <span class="keyword">if</span>(nums[left] == target) <span class="keyword">return</span> left;</span><br><span class="line">    <span class="keyword">if</span>(nums[right] == target) <span class="keyword">return</span> right;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h3><p>给定一个n个元素有序的（升序）整型数组nums和一个目标值target，写一个函数搜索nums中的target，如果目标值存在返回下标，否则返回-1</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">search</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums, <span class="keyword">int</span> target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.empty()) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">int</span> left=<span class="number">0</span>;</span><br><span class="line">        <span class="keyword">int</span> right=nums.size()<span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">while</span>(left&lt;=right)&#123;</span><br><span class="line">            <span class="keyword">int</span> mid = left + (right-left)/<span class="number">2</span>; <span class="comment">//中间值</span></span><br><span class="line">            <span class="keyword">if</span>(nums[mid]&gt;target)&#123;</span><br><span class="line">                right=mid<span class="number">-1</span>;  <span class="comment">//右边界重新赋值</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span>(nums[mid]&lt;target)&#123;</span><br><span class="line">                left=mid+<span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="keyword">return</span> mid;</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">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><hr><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://leetcode-cn.com/leetbook/detail/data-structure-binary-tree/" target="_blank" rel="noopener">leetcode-二叉树</a></li><li><a href="https://www.jianshu.com/p/bce71b2bdbc8" target="_blank" rel="noopener">数据结构：图（Graph）</a></li><li><a href="https://leetcode-cn.com/leetbook/detail/hash-table/" target="_blank" rel="noopener">leetcode-哈希表</a></li><li><a href="https://leetcode-cn.com/leetbook/read/binary-search/xewjg7/" target="_blank" rel="noopener">leetcode-二分查找</a></li><li><a href="https://leetcode-cn.com/leetbook/read/introduction-to-data-structure-binary-search-tree/x6l2am/" target="_blank" rel="noopener">leetcode-二叉搜索树</a></li><li><a href="https://weread.qq.com/web/reader/6ad320e05a04296ad3517d0ke3632bd0222e369853df322" target="_blank" rel="noopener">《数据结构（C++语言版）》</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;包含树、二叉树、哈希表、二分查找的定义和实现&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/categories/cpp/"/>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/tags/cpp/"/>
    
      <category term="datastructure" scheme="https://alonealive.github.io/Blog/tags/datastructure/"/>
    
  </entry>
  
  <entry>
    <title>数据结构之栈和队列</title>
    <link href="https://alonealive.github.io/Blog/2021/05/19/2021/210519_cpp_DataStructure3/"/>
    <id>https://alonealive.github.io/Blog/2021/05/19/2021/210519_cpp_DataStructure3/</id>
    <published>2021-05-19T11:49:00.000Z</published>
    <updated>2021-05-27T14:33:06.866Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>栈和队列以及相关代码实现（阅读《数据结构（C++语言版）》及leetcode课题）</p></blockquote><a id="more"></a><h2 id="FIFO（先入先出数据结构）"><a href="#FIFO（先入先出数据结构）" class="headerlink" title="FIFO（先入先出数据结构）"></a>FIFO（先入先出数据结构）</h2><blockquote><p>FIFO数据结构中，将首先处理添加到队列中的第一个元素。</p></blockquote><blockquote><p>队列是典型的FIFO数据结构。插入（insert）操作也称作入队（enqueue），新元素始终被添加在队列的末尾。删除（delete）操作也被称为出队（dequeue)。你只能移除第一个元素。</p></blockquote><h3 id="队列实现"><a href="#队列实现" class="headerlink" title="队列实现"></a>队列实现</h3><p>为了实现队列，可以使用<strong>动态数组和指向队列头部的索引</strong>。</p><p>队列应支持两种操作：入队和出队。入队会向队列追加一个新元素，而出队会删除第一个元素。 所以我们需要一个索引来指出起点</p><p>以下是简单的代码实现，但是某些场景效率会很低。随着起始指针的移动，会造成更多的空间浪费。</p><h4 id="cpp"><a href="#cpp" class="headerlink" title="cpp"></a>cpp</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyQueue</span> &#123;</span></span><br><span class="line">    <span class="keyword">private</span>:</span><br><span class="line">        <span class="comment">// store elements</span></span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; data;       </span><br><span class="line">        <span class="comment">// a pointer to indicate the start position</span></span><br><span class="line">        <span class="keyword">int</span> p_start;            </span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        MyQueue() &#123;p_start = <span class="number">0</span>;&#125;</span><br><span class="line">        <span class="comment">/** Insert an element into the queue. Return true if the operation is successful. */</span></span><br><span class="line">        <span class="comment">//入队增加元素</span></span><br><span class="line">        <span class="function"><span class="keyword">bool</span> <span class="title">enQueue</span><span class="params">(<span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">            data.push_back(x);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">/** Delete an element from the queue. Return true if the operation is successful. */</span></span><br><span class="line">        <span class="comment">//出队删除一个元素</span></span><br><span class="line">        <span class="function"><span class="keyword">bool</span> <span class="title">deQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (isEmpty()) &#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">            p_start++;  <span class="comment">//队列头部索引后移</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">/** Get the front item from the queue. */</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">Front</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> data[p_start];</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">/** Checks whether the queue is empty or not. */</span></span><br><span class="line">        <span class="comment">//判断是否为空</span></span><br><span class="line">        <span class="function"><span class="keyword">bool</span> <span class="title">isEmpty</span><span class="params">()</span>  </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> p_start &gt;= data.size();</span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    MyQueue q;</span><br><span class="line">    q.enQueue(<span class="number">5</span>);</span><br><span class="line">    q.enQueue(<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">if</span> (!q.isEmpty()) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; q.Front() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    q.deQueue();</span><br><span class="line">    <span class="keyword">if</span> (!q.isEmpty()) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; q.Front() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    q.deQueue();</span><br><span class="line">    <span class="keyword">if</span> (!q.isEmpty()) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; q.Front() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="java"><a href="#java" class="headerlink" title="java"></a>java</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="comment">// "static void main" must be defined in a public class.</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyQueue</span> </span>&#123;</span><br><span class="line">    <span class="comment">// store elements</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;Integer&gt; data;         </span><br><span class="line">    <span class="comment">// a pointer to indicate the start position</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> p_start;            </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        data = <span class="keyword">new</span> ArrayList&lt;Integer&gt;();</span><br><span class="line">        p_start = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** Insert an element into the queue. Return true if the operation is successful. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">enQueue</span><span class="params">(<span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">        data.add(x);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;;    </span><br><span class="line">    <span class="comment">/** Delete an element from the queue. Return true if the operation is successful. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">deQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty() == <span class="keyword">true</span>) &#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">        p_start++;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** Get the front item from the queue. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">Front</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> data.get(p_start);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** Checks whether the queue is empty or not. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> p_start &gt;= data.size();</span><br><span class="line">    &#125;     </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        MyQueue q = <span class="keyword">new</span> MyQueue();</span><br><span class="line">        q.enQueue(<span class="number">5</span>);</span><br><span class="line">        q.enQueue(<span class="number">3</span>);</span><br><span class="line">        <span class="keyword">if</span> (q.isEmpty() == <span class="keyword">false</span>) &#123;</span><br><span class="line">            System.out.println(q.Front());</span><br><span class="line">        &#125;</span><br><span class="line">        q.deQueue();</span><br><span class="line">        <span class="keyword">if</span> (q.isEmpty() == <span class="keyword">false</span>) &#123;</span><br><span class="line">            System.out.println(q.Front());</span><br><span class="line">        &#125;</span><br><span class="line">        q.deQueue();</span><br><span class="line">        <span class="keyword">if</span> (q.isEmpty() == <span class="keyword">false</span>) &#123;</span><br><span class="line">            System.out.println(q.Front());</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><hr><h3 id="循环队列"><a href="#循环队列" class="headerlink" title="循环队列"></a>循环队列</h3><p>上面的方式简单但是低效。</p><p><strong>更有效的方法是使用循环队列</strong>。具体来说，就是<strong>使用固定大小的数组和两个指针来指示起始位置和结束位置</strong></p><p>目的是重用我们之前提到的被浪费的存储。</p><h4 id="定义和接口"><a href="#定义和接口" class="headerlink" title="定义和接口"></a>定义和接口</h4><blockquote><p>循环队列是一种线性数据结构，其操作表现基于 FIFO（先进先出）原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。</p></blockquote><blockquote><p>循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里，一旦一个队列满了，我们就不能插入下一个元素，即使在队列前面仍有空间。但是使用循环队列，我们能使用这些空间去存储新的值。</p></blockquote><p>你的实现应该支持如下操作：</p><ul><li>MyCircularQueue(k): 构造器，设置队列长度为 k</li><li>Front: 从队首获取元素。如果队列为空，返回 -1</li><li>Rear: 获取队尾元素。如果队列为空，返回 -1</li><li>enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真</li><li>deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真</li><li>isEmpty(): 检查循环队列是否为空</li><li>isFull(): 检查循环队列是否已满</li></ul><h4 id="cpp实现"><a href="#cpp实现" class="headerlink" title="cpp实现"></a>cpp实现</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyCircularQueue</span> &#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> <span class="built_in">queue</span>[<span class="number">1000</span>] = &#123;<span class="number">0</span>&#125;;  <span class="comment">//假设值范围在0-1000内</span></span><br><span class="line">    <span class="keyword">int</span> k = <span class="number">0</span>;      <span class="comment">//队列长度</span></span><br><span class="line">    <span class="keyword">int</span> head = <span class="number">0</span>;   <span class="comment">//头指针</span></span><br><span class="line">    <span class="keyword">int</span> end = <span class="number">-1</span>;   <span class="comment">//尾指针</span></span><br><span class="line">    <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    MyCircularQueue(<span class="keyword">int</span> k) &#123;</span><br><span class="line">        <span class="keyword">this</span>-&gt;k = k;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//入队</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">enQueue</span><span class="params">(<span class="keyword">int</span> value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (count &lt; k) &#123;</span><br><span class="line">            count++;    <span class="comment">//当前入队元素数量，判断队列是否已满</span></span><br><span class="line">            end = (end + <span class="number">1</span>) % k; <span class="comment">//取余，注意end初始化是-1</span></span><br><span class="line">            <span class="built_in">queue</span>[end] = value;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</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="comment">//出队</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">deQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="number">0</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">        count--;  <span class="comment">//队列元素减一</span></span><br><span class="line">        head = (head + <span class="number">1</span>) % k;  <span class="comment">//队列元素后移，即第一次更新是1（移除了下标为0的元素）</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Front</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="number">0</span>) &#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">return</span> <span class="built_in">queue</span>[head]; <span class="comment">//队首元素</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Rear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="number">0</span>) &#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">return</span> <span class="built_in">queue</span>[end]; <span class="comment">//获取队尾元素</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (count == <span class="number">0</span>) &#123;  <span class="comment">//队列元素是0，即空队列</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</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="function"><span class="keyword">bool</span> <span class="title">isFull</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (count == k) &#123; <span class="comment">//队列已满</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</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">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Your MyCircularQueue object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MyCircularQueue* obj = new MyCircularQueue(k);</span></span><br><span class="line"><span class="comment"> * bool param_1 = obj-&gt;enQueue(value);</span></span><br><span class="line"><span class="comment"> * bool param_2 = obj-&gt;deQueue();</span></span><br><span class="line"><span class="comment"> * int param_3 = obj-&gt;Front();</span></span><br><span class="line"><span class="comment"> * int param_4 = obj-&gt;Rear();</span></span><br><span class="line"><span class="comment"> * bool param_5 = obj-&gt;isEmpty();</span></span><br><span class="line"><span class="comment"> * bool param_6 = obj-&gt;isFull();</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    MyCircularQueue circularQueue = <span class="keyword">new</span> MyCircularQueue(<span class="number">3</span>); <span class="comment">// 设置长度为 3</span></span><br><span class="line">    circularQueue.enQueue(<span class="number">1</span>);  <span class="comment">// 返回 true</span></span><br><span class="line">    circularQueue.enQueue(<span class="number">2</span>);  <span class="comment">// 返回 true</span></span><br><span class="line">    circularQueue.enQueue(<span class="number">3</span>);  <span class="comment">// 返回 true</span></span><br><span class="line">    circularQueue.enQueue(<span class="number">4</span>);  <span class="comment">// 返回 false，队列已满</span></span><br><span class="line">    circularQueue.Rear();  <span class="comment">// 返回 3</span></span><br><span class="line">    circularQueue.isFull();  <span class="comment">// 返回 true</span></span><br><span class="line">    circularQueue.deQueue();  <span class="comment">// 返回 true</span></span><br><span class="line">    circularQueue.enQueue(<span class="number">4</span>);  <span class="comment">// 返回 true</span></span><br><span class="line">    circularQueue.Rear();  <span class="comment">// 返回 4</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Leetcode实现方式-C"><a href="#Leetcode实现方式-C" class="headerlink" title="Leetcode实现方式-C++"></a>Leetcode实现方式-C++</h4><blockquote><p>在循环队列中，我们使用一个数组和两个指针（head 和 tail）。 head 表示队列的起始位置，tail 表示队列的结束位置。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><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="class"><span class="keyword">class</span> <span class="title">MyCircularQueue</span> &#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; data;</span><br><span class="line">    <span class="keyword">int</span> head;</span><br><span class="line">    <span class="keyword">int</span> tail;</span><br><span class="line">    <span class="keyword">int</span> size;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">/** Initialize your data structure here. Set the size of the queue to be k. */</span></span><br><span class="line">    MyCircularQueue(<span class="keyword">int</span> k) &#123;</span><br><span class="line">        data.resize(k);  <span class="comment">//重置size</span></span><br><span class="line">        head = <span class="number">-1</span>;</span><br><span class="line">        tail = <span class="number">-1</span>;</span><br><span class="line">        size = k;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Insert an element into the circular queue. Return true if the operation is successful. */</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">enQueue</span><span class="params">(<span class="keyword">int</span> value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull()) &#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 class="keyword">if</span> (isEmpty()) &#123;</span><br><span class="line">            head = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        tail = (tail + <span class="number">1</span>) % size;</span><br><span class="line">        data[tail] = value;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Delete an element from the circular queue. Return true if the operation is successful. */</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">deQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#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 class="keyword">if</span> (head == tail) &#123;</span><br><span class="line">            head = <span class="number">-1</span>;</span><br><span class="line">            tail = <span class="number">-1</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        head = (head + <span class="number">1</span>) % size;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Get the front item from the queue. */</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Front</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#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">return</span> data[head];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Get the last item from the queue. */</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Rear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#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">return</span> data[tail];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Checks whether the circular queue is empty or not. */</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> head == <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Checks whether the circular queue is full or not. */</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">isFull</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> ((tail + <span class="number">1</span>) % size) == head;</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"> * Your MyCircularQueue object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MyCircularQueue obj = new MyCircularQueue(k);</span></span><br><span class="line"><span class="comment"> * bool param_1 = obj.enQueue(value);</span></span><br><span class="line"><span class="comment"> * bool param_2 = obj.deQueue();</span></span><br><span class="line"><span class="comment"> * int param_3 = obj.Front();</span></span><br><span class="line"><span class="comment"> * int param_4 = obj.Rear();</span></span><br><span class="line"><span class="comment"> * bool param_5 = obj.isEmpty();</span></span><br><span class="line"><span class="comment"> * bool param_6 = obj.isFull();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><h4 id="Leetcode实现方式-Java"><a href="#Leetcode实现方式-Java" class="headerlink" title="Leetcode实现方式-Java"></a>Leetcode实现方式-Java</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="class"><span class="keyword">class</span> <span class="title">MyCircularQueue</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span>[] data;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> head;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> tail;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Initialize your data structure here. Set the size of the queue to be k. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyCircularQueue</span><span class="params">(<span class="keyword">int</span> k)</span> </span>&#123;</span><br><span class="line">        data = <span class="keyword">new</span> <span class="keyword">int</span>[k];</span><br><span class="line">        head = -<span class="number">1</span>;</span><br><span class="line">        tail = -<span class="number">1</span>;</span><br><span class="line">        size = k;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Insert an element into the circular queue. Return true if the operation is successful. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">enQueue</span><span class="params">(<span class="keyword">int</span> value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isFull() == <span class="keyword">true</span>) &#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 class="keyword">if</span> (isEmpty() == <span class="keyword">true</span>) &#123;</span><br><span class="line">            head = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        tail = (tail + <span class="number">1</span>) % size;</span><br><span class="line">        data[tail] = value;</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">/** Delete an element from the circular queue. Return true if the operation is successful. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">deQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty() == <span class="keyword">true</span>) &#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 class="keyword">if</span> (head == tail) &#123;</span><br><span class="line">            head = -<span class="number">1</span>;</span><br><span class="line">            tail = -<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">        head = (head + <span class="number">1</span>) % size;</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">/** Get the front item from the queue. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">Front</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty() == <span class="keyword">true</span>) &#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">return</span> data[head];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Get the last item from the queue. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">Rear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty() == <span class="keyword">true</span>) &#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">return</span> data[tail];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Checks whether the circular queue is empty or not. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> head == -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Checks whether the circular queue is full or not. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isFull</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> ((tail + <span class="number">1</span>) % size) == head;</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"> * Your MyCircularQueue object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MyCircularQueue obj = new MyCircularQueue(k);</span></span><br><span class="line"><span class="comment"> * boolean param_1 = obj.enQueue(value);</span></span><br><span class="line"><span class="comment"> * boolean param_2 = obj.deQueue();</span></span><br><span class="line"><span class="comment"> * int param_3 = obj.Front();</span></span><br><span class="line"><span class="comment"> * int param_4 = obj.Rear();</span></span><br><span class="line"><span class="comment"> * boolean param_5 = obj.isEmpty();</span></span><br><span class="line"><span class="comment"> * boolean param_6 = obj.isFull();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><hr><h3 id="☆内置队列库"><a href="#☆内置队列库" class="headerlink" title="☆内置队列库"></a>☆内置队列库</h3><p>大多数流行语言都提供内置的队列库，因此您无需重新发明轮子。</p><p>队列有两个重要的操作，入队 enqueue 和出队 dequeue。 此外，我们应该能够获得队列中的第一个元素，因为应该首先处理它。</p><h4 id="cpp-1"><a href="#cpp-1" class="headerlink" title="cpp"></a>cpp</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 1. Initialize a queue.</span></span><br><span class="line">    <span class="built_in">queue</span>&lt;<span class="keyword">int</span>&gt; q;</span><br><span class="line">    <span class="comment">// 2. Push new element.</span></span><br><span class="line">    q.push(<span class="number">5</span>);</span><br><span class="line">    q.push(<span class="number">13</span>);</span><br><span class="line">    q.push(<span class="number">8</span>);</span><br><span class="line">    q.push(<span class="number">6</span>);</span><br><span class="line">    <span class="comment">// 3. Check if queue is empty.</span></span><br><span class="line">    <span class="keyword">if</span> (q.empty()) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Queue is empty!"</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 4. Pop an element.移除一个元素</span></span><br><span class="line">    q.pop();</span><br><span class="line">    <span class="comment">// 5. Get the first element.获取第一个元素</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"The first element is: "</span> &lt;&lt; q.front() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="comment">// 6. Get the last element.获取最后一个元素</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"The last element is: "</span> &lt;&lt; q.back() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="comment">// 7. Get the size of the queue.长度</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"The size is: "</span> &lt;&lt; q.size() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="java-1"><a href="#java-1" class="headerlink" title="java"></a>java</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">// "static void main" must be defined in a public class.</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 1. Initialize a queue.</span></span><br><span class="line">        Queue&lt;Integer&gt; q = <span class="keyword">new</span> LinkedList();</span><br><span class="line">        <span class="comment">// 2. Get the first element - return null if queue is empty.</span></span><br><span class="line">        System.out.println(<span class="string">"The first element is: "</span> + q.peek());</span><br><span class="line">        <span class="comment">// 3. Push new element.</span></span><br><span class="line">        q.offer(<span class="number">5</span>);</span><br><span class="line">        q.offer(<span class="number">13</span>);</span><br><span class="line">        q.offer(<span class="number">8</span>);</span><br><span class="line">        q.offer(<span class="number">6</span>);</span><br><span class="line">        <span class="comment">// 4. Pop an element.</span></span><br><span class="line">        q.poll();</span><br><span class="line">        <span class="comment">// 5. Get the first element.</span></span><br><span class="line">        System.out.println(<span class="string">"The first element is: "</span> + q.peek());</span><br><span class="line">        <span class="comment">// 7. Get the size of the queue.</span></span><br><span class="line">        System.out.println(<span class="string">"The size is: "</span> + q.size());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="LIFO（后入先出数据结构）"><a href="#LIFO（后入先出数据结构）" class="headerlink" title="LIFO（后入先出数据结构）"></a>LIFO（后入先出数据结构）</h2><blockquote><p>参考：<a href="https://leetcode-cn.com/leetbook/read/queue-stack/ghqur/" target="_blank" rel="noopener">leetcode-后入先出的数据结构</a></p><p>在LIFO数据结构中，<strong>将首先处理添加到队列中的最新元素</strong>。</p></blockquote><blockquote><p>与队列不同，栈是一个LIFO数据结构。通常，插入操作在栈中被称作入栈push。与队列类似，总是在堆栈的末尾添加一个新元素。<strong>但是，删除操作，退栈pop，将始终删除队列中相对于它的最后一个元素</strong></p></blockquote><hr><h3 id="栈定义和实现"><a href="#栈定义和实现" class="headerlink" title="栈定义和实现"></a>栈定义和实现</h3><blockquote><p>栈是限制在表的一端进行插入和删除的线性表。表中允许插入、删除的这一端称为栈顶，栈顶的当前位置是动态变化的；不允许插入和删除的另一端称为栈底，栈底是固定不变的。当表中没有元素时称为空栈。栈的插入运算称为进栈、压栈或入栈，栈的删除运算称为退栈或出栈。</p></blockquote><h4 id="cpp-2"><a href="#cpp-2" class="headerlink" title="cpp"></a>cpp</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyStack</span> &#123;</span></span><br><span class="line">    <span class="keyword">private</span>:</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; data;               <span class="comment">// store elements</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        <span class="comment">/** Insert an element into the stack. */</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">push</span><span class="params">(<span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">            data.push_back(x);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">/** Checks whether the queue is empty or not. */</span></span><br><span class="line">        <span class="function"><span class="keyword">bool</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> data.empty();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">/** Get the top item from the queue. */</span></span><br><span class="line">        <span class="function"><span class="keyword">int</span> <span class="title">top</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> data.back();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">/** Delete an element from the queue. Return true if the operation is successful. */</span></span><br><span class="line">        <span class="function"><span class="keyword">bool</span> <span class="title">pop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (isEmpty()) &#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">            data.pop_back();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    MyStack s;</span><br><span class="line">    s.push(<span class="number">1</span>);</span><br><span class="line">    s.push(<span class="number">2</span>);</span><br><span class="line">    s.push(<span class="number">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; <span class="number">4</span>; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!s.isEmpty()) &#123;</span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; s.top() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; (s.pop() ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="java-2"><a href="#java-2" class="headerlink" title="java"></a>java</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// "static void main" must be defined in a public class.</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyStack</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;Integer&gt; data;               <span class="comment">// store elements</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyStack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        data = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** Insert an element into the stack. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">push</span><span class="params">(<span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">        data.add(x);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** Checks whether the queue is empty or not. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> data.isEmpty();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** Get the top item from the queue. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">top</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> data.get(data.size() - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** Delete an element from the queue. Return true if the operation is successful. */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">pop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) &#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">        data.remove(data.size() - <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">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        MyStack s = <span class="keyword">new</span> MyStack();</span><br><span class="line">        s.push(<span class="number">1</span>);</span><br><span class="line">        s.push(<span class="number">2</span>);</span><br><span class="line">        s.push(<span class="number">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; <span class="number">4</span>; ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!s.isEmpty()) &#123;</span><br><span class="line">                System.out.println(s.top());</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(s.pop());</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><hr><h3 id="☆内置栈库"><a href="#☆内置栈库" class="headerlink" title="☆内置栈库"></a>☆内置栈库</h3><h4 id="cpp-3"><a href="#cpp-3" class="headerlink" title="cpp"></a>cpp</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 1. Initialize a stack.</span></span><br><span class="line">    <span class="built_in">stack</span>&lt;<span class="keyword">int</span>&gt; s;</span><br><span class="line">    <span class="comment">// 2. Push new element.</span></span><br><span class="line">    s.push(<span class="number">5</span>);</span><br><span class="line">    s.push(<span class="number">13</span>);</span><br><span class="line">    s.push(<span class="number">8</span>);</span><br><span class="line">    s.push(<span class="number">6</span>);</span><br><span class="line">    <span class="comment">// 3. Check if stack is empty.</span></span><br><span class="line">    <span class="keyword">if</span> (s.empty()) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Stack is empty!"</span> &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 4. Pop an element.</span></span><br><span class="line">    s.pop();</span><br><span class="line">    <span class="comment">// 5. Get the top element.</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"The top element is: "</span> &lt;&lt; s.top() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="comment">// 6. Get the size of the stack.</span></span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"The size is: "</span> &lt;&lt; s.size() &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="java-3"><a href="#java-3" class="headerlink" title="java"></a>java</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="comment">// "static void main" must be defined in a public class.</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 1. Initialize a stack.</span></span><br><span class="line">        Stack&lt;Integer&gt; s = <span class="keyword">new</span> Stack&lt;&gt;();</span><br><span class="line">        <span class="comment">// 2. Push new element.</span></span><br><span class="line">        s.push(<span class="number">5</span>);</span><br><span class="line">        s.push(<span class="number">13</span>);</span><br><span class="line">        s.push(<span class="number">8</span>);</span><br><span class="line">        s.push(<span class="number">6</span>);</span><br><span class="line">        <span class="comment">// 3. Check if stack is empty.</span></span><br><span class="line">        <span class="keyword">if</span> (s.empty() == <span class="keyword">true</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">"Stack is empty!"</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">// 4. Pop an element.</span></span><br><span class="line">        s.pop();</span><br><span class="line">        <span class="comment">// 5. Get the top element.</span></span><br><span class="line">        System.out.println(<span class="string">"The top element is: "</span> + s.peek());</span><br><span class="line">        <span class="comment">// 6. Get the size of the stack.</span></span><br><span class="line">        System.out.println(<span class="string">"The size is: "</span> + s.size());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><p>栈的存储与一般的线性表的存储类似，主要有两种存储方式：顺序存储和链式存储。</p><h2 id="顺序栈"><a href="#顺序栈" class="headerlink" title="顺序栈"></a>顺序栈</h2><blockquote><p>利用顺序存储方式实现的栈称为顺序栈。<br>类似于顺序表的定义，要分配一块连续的存储空间存放栈中的元素，栈底位置可以固定地设置在数组的任何一端（一般在下标为0的一端），而栈顶是随着插入和删除而变化的，再用一个变量指明当前栈顶的位置（实际上是栈顶元素的下一个位置）</p></blockquote><p>类描述：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">int</span> DataType;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SeqStack</span> &#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    DataType *base; <span class="comment">//栈底指针</span></span><br><span class="line">    DataType *top;  <span class="comment">//栈顶指针，始终指向栈顶元素的后一个位置</span></span><br><span class="line">    <span class="keyword">int</span> size;       <span class="comment">//栈大小</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">//构造一个空栈</span></span><br><span class="line">    SeqStack(<span class="keyword">int</span> stacksize=<span class="number">100</span>) &#123;</span><br><span class="line">        base = <span class="keyword">new</span> DataType[stacksize];</span><br><span class="line">        top=base;   <span class="comment">//只想栈顶元素的后一个位置</span></span><br><span class="line">        size=stacksize;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//销毁一个已存在的栈</span></span><br><span class="line">    ~SeqStack() &#123;</span><br><span class="line">        <span class="keyword">delete</span>[] base;</span><br><span class="line">        top=<span class="literal">NULL</span>;</span><br><span class="line">        base=<span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Empty_Stack</span><span class="params">()</span></span>;   <span class="comment">//判断栈是否为空</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Push_Stack</span><span class="params">(DataType e)</span></span>;   <span class="comment">//将元素e插入栈顶</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Pop_Stack</span><span class="params">(DataType &amp;e)</span></span>;   <span class="comment">//从栈顶删除一个元素到e中返回</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">GetTop_Stack</span><span class="params">(DataType &amp;e)</span></span>; <span class="comment">//从栈顶取出一个元素到e中返回</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="基本运算"><a href="#基本运算" class="headerlink" title="基本运算"></a>基本运算</h3><h4 id="判断栈是否为空"><a href="#判断栈是否为空" class="headerlink" title="判断栈是否为空"></a>判断栈是否为空</h4><p>算法思想：判断top是否小于等于base，小于等于则为空栈，返回1，否则返回0</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SeqStack::Empty_Stack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span>((top &lt;= base));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="入栈"><a href="#入栈" class="headerlink" title="入栈"></a>入栈</h4><p>入栈操作是在栈的顶部进行插入操作，相当于在顺序表的表尾进行插入，因而无须移动元素。</p><p>算法思想：首先判断栈是否已满，若满则失败，返回0；否则，由于栈的top 指向栈顶元素的后一个位置，将入栈元素赋到top的位置，再将top+1指向新的位置，成功返回1</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SeqStack::Push_Stack</span><span class="params">(DataType e)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> ((top-base) &lt; size) &#123;  <span class="comment">//是否栈满</span></span><br><span class="line">        *top = e;</span><br><span class="line">        top++;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125; <span class="keyword">else</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></pre></td></tr></table></figure><h4 id="出栈"><a href="#出栈" class="headerlink" title="出栈"></a>出栈</h4><p>出栈操作是在栈的顶部进行删除操作，相当于在顺序表的表尾进行删除，因而也无须移动元素。</p><p>算法思想：首先判断栈是否为空，若空则失败，返回0；否则，由于栈的top 指向栈顶元素的后一位置，要先修改top为top-1，再将其所指向的元素以引用参数e返回，成功返回1。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SeqStack::Pop_Stack</span><span class="params">(DataType &amp;e)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (top &gt; base) &#123;  <span class="comment">//是否栈为空</span></span><br><span class="line">        top--;</span><br><span class="line">        e = *top;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125; <span class="keyword">else</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></pre></td></tr></table></figure><h4 id="取栈顶元素操作"><a href="#取栈顶元素操作" class="headerlink" title="取栈顶元素操作"></a>取栈顶元素操作</h4><p>取栈顶元素是获取出栈顶元素的值，而不改变栈。</p><p>算法思想：首先判断栈是否为空，若空则失败，返回0；否则，由于栈的top 指向栈顶元素的后一位置，返回top-1所指单元的值，栈不发生变化。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SeqStack::Get_Stack</span><span class="params">(DataType &amp;e)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (top &gt; base) &#123;  <span class="comment">//是否栈为空</span></span><br><span class="line">        e = *(top<span class="number">-1</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125; <span class="keyword">else</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></pre></td></tr></table></figure><hr><h2 id="链栈"><a href="#链栈" class="headerlink" title="链栈"></a>链栈</h2><blockquote><p>用链式存储结构的栈，节点结构和单链表相同</p></blockquote><p>因为栈中的主要运算是在栈顶进行插入和删除操作，显然在单链表的表头插入和删除都比较方便</p><p>因此以其作为栈顶，而且没有必要像单链表那样为了运算方便而附加一个头结点</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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">typedf <span class="keyword">int</span> DataType;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StackNode</span> &#123;</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        DataType data;</span><br><span class="line">        StackNode *next;</span><br><span class="line">        StackNode() &#123;</span><br><span class="line">            next = <span class="literal">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="class"><span class="keyword">class</span> <span class="title">LinkStack</span> &#123;</span></span><br><span class="line">    <span class="keyword">private</span>:</span><br><span class="line">        StackNode *top;</span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        LinkStack() &#123;</span><br><span class="line">            top = <span class="literal">NULL</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ~LinkStack() &#123;</span><br><span class="line">            <span class="keyword">while</span>(top) &#123;</span><br><span class="line">                p = top;</span><br><span class="line">                top = top-&gt;next;</span><br><span class="line">                <span class="keyword">delete</span> p;</span><br><span class="line">            &#125;</span><br><span class="line">            top = <span class="literal">NULL</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Empty_Stack</span><span class="params">()</span></span>;              <span class="comment">//判空</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Push_Stack</span><span class="params">(DataType e)</span></span>;     <span class="comment">//将元素e插入栈顶</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">Pop_Satck</span><span class="params">(DataType &amp;e)</span></span>;     <span class="comment">//从栈顶删除一个元素到e中返回</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">GetTop_Stack</span><span class="params">(DataType &amp;e)</span></span>;  <span class="comment">//从栈顶去除元素到e中返回</span></span><br><span class="line">&#125;；</span><br></pre></td></tr></table></figure><hr><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://leetcode-cn.com/leetbook/detail/queue-stack/c" target="_blank" rel="noopener">Leetcode-队列&amp;栈</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;栈和队列以及相关代码实现（阅读《数据结构（C++语言版）》及leetcode课题）&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/categories/cpp/"/>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/tags/cpp/"/>
    
      <category term="datastructure" scheme="https://alonealive.github.io/Blog/tags/datastructure/"/>
    
  </entry>
  
  <entry>
    <title>数据结构之链表</title>
    <link href="https://alonealive.github.io/Blog/2021/05/16/2021/210516_cpp_DataStructure2/"/>
    <id>https://alonealive.github.io/Blog/2021/05/16/2021/210516_cpp_DataStructure2/</id>
    <published>2021-05-16T10:05:00.000Z</published>
    <updated>2021-05-27T14:33:03.130Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>链表基本运算实现以及快慢指针应用</p></blockquote><a id="more"></a><h2 id="单链表"><a href="#单链表" class="headerlink" title="单链表"></a>单链表</h2><blockquote><p>单链表中的每个结点不仅包含值，还包含链接到下一个结点的引用字段。通过这种方式，单链表将所有结点按顺序组织起来。</p></blockquote><p>单链表中的节点应该具有两个属性：val和next。</p><ul><li>val是当前节点的值</li><li>next 是指向下一个节点的指针/引用</li><li>如果要使用双向链表，则还需要一个属性prev以指示链表中的上一个节点。</li></ul><p>定义：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Definition for singly-linked list.</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">SinglyListNode</span> &#123;</span></span><br><span class="line">    <span class="keyword">int</span> val;</span><br><span class="line">    SinglyListNode *next;</span><br><span class="line">    SinglyListNode(<span class="keyword">int</span> x) : val(x), next(<span class="literal">NULL</span>) &#123;&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="函数功能"><a href="#函数功能" class="headerlink" title="函数功能"></a>函数功能</h3><ul><li>get(index)：获取链表中第 index 个节点的值。如果索引无效，则返回-1。</li><li>addAtHead(val)：在链表的第一个元素之前添加一个值为 val 的节点。插入后，新节点将成为链表的第一个节点。</li><li>addAtTail(val)：将值为 val 的节点追加到链表的最后一个元素。</li><li>addAtIndex(index,val)：在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度，则该节点将附加到链表的末尾。如果 index 大于链表长度，则不会插入节点。如果index小于0，则在头部插入节点。</li><li>deleteAtIndex(index)：如果索引 index 有效，则删除链表中的第 index 个节点。</li></ul><h3 id="C-函数实现"><a href="#C-函数实现" class="headerlink" title="C++函数实现"></a>C++函数实现</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyLinkedList</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">/** Initialize your data structure here. */</span></span><br><span class="line">    MyLinkedList() &#123;</span><br><span class="line">    size = <span class="number">0</span>;</span><br><span class="line">    head = <span class="literal">nullptr</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(index &gt;= size || index &lt; <span class="number">0</span> || head == <span class="literal">nullptr</span>) &#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">        SinglyListNode *temp = head;  <span class="comment">//从第一个节点开始</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i &lt; index; ++i) &#123;</span><br><span class="line">          temp = temp-&gt;next;  <span class="comment">//遍历查找</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> temp-&gt;val; <span class="comment">//返回该index的val值</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addAtHead</span><span class="params">(<span class="keyword">int</span> val)</span> </span>&#123;</span><br><span class="line">        SinglyListNode *temp = <span class="keyword">new</span> SinglyListNode(val);  <span class="comment">//new一个val值的结点</span></span><br><span class="line">        temp-&gt;next = head; <span class="comment">//加在链表第一个元素之前</span></span><br><span class="line">        temp = <span class="literal">nullptr</span>;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Append a node of value val to the last element of the linked list. */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addAtTail</span><span class="params">(<span class="keyword">int</span> val)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (size == <span class="number">0</span>) &#123;</span><br><span class="line">        head = <span class="keyword">new</span> SinglyListNode(val);  <span class="comment">//新建第一个结点</span></span><br><span class="line">        ++size;</span><br><span class="line">      &#125;</span><br><span class="line">      SinglyListNode *find = head;</span><br><span class="line">      <span class="keyword">while</span> (find-&gt;next != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        find = find-&gt;next;  <span class="comment">//遍历到最后一个结点</span></span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      SinglyListNode *temp = <span class="keyword">new</span> SinglyListNode(val);</span><br><span class="line">      find-&gt;next = temp;  <span class="comment">//最后一个节点的指针指向需要添加的这个元素</span></span><br><span class="line">      size++;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addAtIndex</span><span class="params">(<span class="keyword">int</span> index, <span class="keyword">int</span> val)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (index &gt; size) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (index &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">        addAtHead(val);  <span class="comment">//添加为第一个元素之前</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">      SinglyListNode *pr = head;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i &lt; index<span class="number">-1</span>; ++i) &#123;</span><br><span class="line">        pr = pr-&gt;next;</span><br><span class="line">      &#125;</span><br><span class="line">      </span><br><span class="line">      SinglyListNode *cur = <span class="keyword">new</span> SinglyListNode(val);</span><br><span class="line">      cur-&gt;next = pr-&gt;next;  <span class="comment">//插入到查找到的index前面</span></span><br><span class="line">      pr-&gt;next = cur;</span><br><span class="line">      size++;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/** Delete the index-th node in the linked list, if the index is valid. */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">deleteAtIndex</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (index &gt; size) <span class="keyword">return</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (index &lt; <span class="number">0</span>)    <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (index == <span class="number">0</span>) &#123;</span><br><span class="line">          head = head-&gt;next;</span><br><span class="line">          size--;</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">        SinglyListNode *del = head;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i &lt; index<span class="number">-1</span>; ++i) &#123;</span><br><span class="line">          del = del-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">        del-&gt;next = del-&gt;next-&gt;next;</span><br><span class="line">        size--;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*  打印链表 */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (size == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"empty"</span> &lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      SinglyListNode *pr = head;</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="built_in">cout</span> &lt;&lt; <span class="string">"Node "</span> &lt;&lt; i &lt;&lt; <span class="string">" : "</span> &lt;&lt; pr-&gt;val &lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">        pr = pr-&gt;next;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">~MyLinkedList() &#123;</span><br><span class="line">size = <span class="number">0</span>;</span><br><span class="line">head = <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">      <span class="class"><span class="keyword">struct</span> <span class="title">SinglyListNode</span> &#123;</span></span><br><span class="line">        <span class="keyword">int</span> val;</span><br><span class="line">        SinglyListNode *next;</span><br><span class="line">        SinglyListNode(<span class="keyword">int</span> x) : val(x), next(<span class="literal">nullptr</span>) &#123;&#125;</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="keyword">int</span> size;</span><br><span class="line">      SinglyListNode* head;</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"> * Your MyLinkedList object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MyLinkedList* obj = new MyLinkedList();</span></span><br><span class="line"><span class="comment"> * int param_1 = obj-&gt;get(index);</span></span><br><span class="line"><span class="comment"> * obj-&gt;addAtHead(val);</span></span><br><span class="line"><span class="comment"> * obj-&gt;addAtTail(val);</span></span><br><span class="line"><span class="comment"> * obj-&gt;addAtIndex(index,val);</span></span><br><span class="line"><span class="comment"> * obj-&gt;deleteAtIndex(index);</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">//new对象，调用方法</span></span><br><span class="line">MyLinkedList linkedList = <span class="keyword">new</span> MyLinkedList();</span><br><span class="line">linkedList.addAtHead(<span class="number">1</span>);</span><br><span class="line">linkedList.addAtTail(<span class="number">3</span>);</span><br><span class="line">linkedList.addAtIndex(<span class="number">1</span>,<span class="number">2</span>);   <span class="comment">//链表变为1-&gt; 2-&gt; 3</span></span><br><span class="line">linkedList.get(<span class="number">1</span>);            <span class="comment">//返回2</span></span><br><span class="line">linkedList.deleteAtIndex(<span class="number">1</span>);  <span class="comment">//现在链表是1-&gt; 3</span></span><br><span class="line">linkedList.get(<span class="number">1</span>);            <span class="comment">//返回3</span></span><br></pre></td></tr></table></figure><h3 id="C-的i-和-i"><a href="#C-的i-和-i" class="headerlink" title="C++的i++和++i"></a>C++的i++和++i</h3><p>i++ ：先引用后增加，先在i所在的表达式中使用i的当前值，后让i加1</p><p>++i ：先增加后引用，让i先加1，然后在i所在的表达式中使用i的新值</p><p>即：</p><ul><li>++i是将i的值先+1，然后返回i的值</li><li>i++是先将i的值存到寄存器里，然后执行i+1，然后返回寄存器里的值。</li></ul><hr><h2 id="链表双指针模板（快慢指针java-amp-cpp）"><a href="#链表双指针模板（快慢指针java-amp-cpp）" class="headerlink" title="链表双指针模板（快慢指针java&amp;cpp）"></a>链表双指针模板（快慢指针java&amp;cpp）</h2><p>快慢指针可用于环形链表、相交链表的查询等运算</p><p><strong>注意以下点：</strong></p><ol><li>在调用 next 字段之前，始终检查节点是否为空。获取空节点的下一个节点将导致空指针错误。例如，在我们运行<code>fast = fast.next.next</code>之前，需要检查<code>fast</code>和<code>fast.next</code>不为空</li><li>仔细定义循环的结束条件</li></ol><ul><li>C++语言版</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Initialize slow &amp; fast pointers</span></span><br><span class="line">ListNode* slow = head;</span><br><span class="line">ListNode* fast = head;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Change this condition to fit specific problem.</span></span><br><span class="line"><span class="comment"> * Attention: remember to avoid null-pointer error</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">while</span> (slow &amp;&amp; fast &amp;&amp; fast-&gt;next) &#123;</span><br><span class="line">    slow = slow-&gt;next;          <span class="comment">// move slow pointer one step each time</span></span><br><span class="line">    fast = fast-&gt;next-&gt;next;    <span class="comment">// move fast pointer two steps each time</span></span><br><span class="line">    <span class="keyword">if</span> (slow == fast) &#123;         <span class="comment">// change this condition to fit specific problem</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">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="literal">false</span>;   <span class="comment">// change return value to fit specific problem</span></span><br></pre></td></tr></table></figure><ul><li>Java语言：</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Initialize slow &amp; fast pointers</span></span><br><span class="line">ListNode slow = head;</span><br><span class="line">ListNode fast = head;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Change this condition to fit specific problem.</span></span><br><span class="line"><span class="comment"> * Attention: remember to avoid null-pointer error</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">while</span> (slow != <span class="keyword">null</span> &amp;&amp; fast != <span class="keyword">null</span> &amp;&amp; fast.next != <span class="keyword">null</span>) &#123;</span><br><span class="line">    slow = slow.next;           <span class="comment">// move slow pointer one step each time</span></span><br><span class="line">    fast = fast.next.next;      <span class="comment">// move fast pointer two steps each time</span></span><br><span class="line">    <span class="keyword">if</span> (slow == fast) &#123;         <span class="comment">// change this condition to fit specific problem</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">false</span>;   <span class="comment">// change return value to fit specific problem</span></span><br></pre></td></tr></table></figure><p>在环形链表的查找循环中，假设我们每次移动较快的指针 2 步，每次移动较慢的指针 1 步：</p><ul><li>如果没有循环，快指针需要 N/2 次才能到达链表的末尾，其中 N 是链表的长度。</li><li>如果存在循环，则快指针需要 M 次才能赶上慢指针，其中 M 是列表中循环的长度</li></ul><p>显然，<code>M &lt;= N</code>。所以我们将循环运行N次。该算法的时间复杂度总共为O(N)</p><hr><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://leetcode-cn.com/leetbook/read/linked-list/jsumh/" target="_blank" rel="noopener">leetcode-链表</a></li><li><a href="https://leetcode-cn.com/problems/design-linked-list/solution/cdan-lian-biao-shi-xian-xiang-xi-zhu-shi-by-carlsu/" target="_blank" rel="noopener">链表设计实现详解</a></li><li><a href="https://blog.csdn.net/u012679707/article/details/80313425" target="_blank" rel="noopener">C++的i++和++i详解</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;链表基本运算实现以及快慢指针应用&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/categories/cpp/"/>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/tags/cpp/"/>
    
      <category term="datastructure" scheme="https://alonealive.github.io/Blog/tags/datastructure/"/>
    
  </entry>
  
  <entry>
    <title>数据结构之时间复杂度和线性表</title>
    <link href="https://alonealive.github.io/Blog/2021/05/14/2021/210514_cpp_DataStructure1/"/>
    <id>https://alonealive.github.io/Blog/2021/05/14/2021/210514_cpp_DataStructure1/</id>
    <published>2021-05-14T12:49:00.000Z</published>
    <updated>2021-05-22T11:29:40.821Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>《数据结构（C++语言版）》书，关于数据结构再读记录。主要包含数据结构的基本概念、算法概念、时间复杂度、线性表（顺序表&amp;链表）</p></blockquote><a id="more"></a><h2 id="基本概念术语"><a href="#基本概念术语" class="headerlink" title="基本概念术语"></a>基本概念术语</h2><ul><li>数据（Data）：对客观事物的符号表示，它能被计算机识别、存储和加工处理。含义广泛，声音、图像、视频都属于数据的范畴。</li><li>数据元素（Data Element）：数据的基本单位，有时也称为元素、结点、顶点、记录。</li><li>数据项：一个数据元素可能由若干数据项（Data Item）组成。数据项是最小标识单位，有时也称为字段、域或属性。数据元素也可以仅有一个数据项。</li><li><strong>数据结构（Data Structure）</strong>：指数据元素之间的相互关系，即数据的组织形式。一般包含三方面内容：</li></ul><ol><li>数据元素之间的逻辑关系，也称为数据的逻辑结构（Logical Structure）</li><li>数据元素及逻辑关系在计算机存储器内的表示方式，称为数据的存储结构（Storage Structure）</li><li>数据运算，即对数据施加的操作。运算的定义直接依赖于逻辑结构，但运算的实现必须依赖于存储结构</li></ol><h3 id="数据的逻辑结构"><a href="#数据的逻辑结构" class="headerlink" title="数据的逻辑结构"></a>数据的逻辑结构</h3><p>数据中元素通常有下列四种形式的逻辑关系：</p><ol><li>集合：每个元素都是独立的，相互之间没有逻辑关系</li><li>线性结构：结构中的元素之间存在一对一的关系，即所谓的线性关系</li><li>树形结构：结构中的数据元素之间存在一对多的关系</li><li>图状结构：结构中的元素之间存在多对多的关系</li></ol><p>通常将集合、树形结构、图状结构归纳为非线性结构。因此，数据的逻辑结构可分为两大类，即线性结构和非线性结构。</p><h3 id="数据的存储结构（物理实现方面）"><a href="#数据的存储结构（物理实现方面）" class="headerlink" title="数据的存储结构（物理实现方面）"></a>数据的存储结构（物理实现方面）</h3><p>四种：</p><ol><li>顺序存储：将数据元素依次存储于一组地址连续的存储单元中，<strong>元素间的逻辑关系由存储单元的位置直接体现</strong>，由此得到的存储表示称为顺序存储结构（Sequential Storage Structure）。高级语言中，<strong>常用一维数组来实现顺序存储结构</strong>。该方法主要用于线性结构。非线性结构也可通过某种线性化的处理，实现顺序存储。</li><li>链接存储：将数据元素存储在一组任意的存储单元中，<strong>用附加的指针域表示元素之间的逻辑关系</strong>，由此得到的存储表示称为链接存储（Linked StorageStructure）。使用这种存储结构时，往往把一个数据元素及附加的指针一起称作一个结点。高级语言中，常用指针变量实现链接存储。</li><li>索引存储：该方法的特点是在存储数据元素的同时，还可以建立附加的索引表（通俗讲，相当于目录）。索引表中每一项称为索引项。索引项的一般形式是：（关键字，地址）。关键字是指能唯一标识数据元素的数据项。若每个数据元素在索引表中均有一个索引项，则该索引表称为稠密索引（Dense Index）。若一个索引项对应一组数据元素，则该索引表称为稀疏索引（Sparse Index）。</li><li>散列存储：该方法是依据数据元素的关键字，用一个事先设计好的函数计算出该数据元素的存储地址，然后把它存入该地址中。这种函数称为散列函数（即Hash），由散列函数计算出的地址称为散列地址。</li></ol><h3 id="数据运算"><a href="#数据运算" class="headerlink" title="数据运算"></a>数据运算</h3><p>最常用的基本运算有检索（查找）、插入、删除、更新、排序等。</p><p>综上，数据结构包含逻辑结构、存储结构和运算三方面的内容。<strong>同一逻辑结构采用不同存储结构，得到的是不同的数据结构，常用不同的数据结构名来标识它们（1）；而同一逻辑结构定义不同的运算也会导致不同的数据结构（2）</strong></p><p>举几个例子：</p><ol><li>线性结构采用顺序存储时称为顺序表，采用链接存储时则称为链表</li><li>若限制线性结构的插入、删除在一端进行，则该结构称为栈（先进后出）；若限制插入在一端进行，而删除在另一端进行，则称为队列（先进先出）</li><li>若栈采用顺序存储结构，则称为顺序栈；若栈采用链式存储结构，则称为链栈。顺序栈与链栈也是两种不同的数据结构</li></ol><hr><h2 id="数据类型（Data-Type）"><a href="#数据类型（Data-Type）" class="headerlink" title="数据类型（Data Type）"></a>数据类型（Data Type）</h2><blockquote><p>数据类型是一个值的集合和在这个集合上定义的一组操作的总称。例如，C++中的整型变量，其值集为某个区间上的整数（区间大小依赖于不同的机器），定义在其上的操作为加、减、乘、除和取模等运算</p></blockquote><p>按“值”可否分解，可把数据类型分为两类:</p><ol><li>原子类型：其值不可分解，如C++的基本类型（整型、字符型、实型、枚举型）、指针类型和空类型</li><li>结构类型：其值可分解成若干成分（或称分量），如C++的数组类型、结构类型等。结构类型的成分可以是原子类型，也可以是某种结构类型。可以把数据类型看作程序设计语言已实现的数据结构</li></ol><h3 id="抽象数据类型"><a href="#抽象数据类型" class="headerlink" title="抽象数据类型"></a>抽象数据类型</h3><p>抽象数据类型（Abstract Data Type, ADT）是指一个数学模型，以及定义在该模型上的一组操作。抽象数据类型的定义取决于它的一组逻辑特性，而与其在计算机内部如何表示和实现无关。</p><p>抽象数据类型的定义可以由一种数据结构和定义在其上的一组操作组成，而数据结构又包括数据元素间的关系，因此抽象数据类型一般可以由元素、关系及操作三种要素来定义。</p><hr><h2 id="算法"><a href="#算法" class="headerlink" title="算法"></a>算法</h2><blockquote><p>沃斯（N.Wirth）的公式<strong>数据结构+算法=程序</strong></p><p>数据运算是通过算法来描述的。算法（Algorithm）是对特定问题求解步骤的描述，是指令的有限序列，其中每条指令表示一个或多个操作</p></blockquote><h3 id="算法的五个特性"><a href="#算法的五个特性" class="headerlink" title="算法的五个特性"></a>算法的五个特性</h3><p>算法必须具备五个特性：</p><ol><li>有穷性：一个算法对于任何合法的输入必须在执行有穷步骤之后结束，且每步都可在有限时间内完成</li><li>确定性：算法的每条指令必须有确切含义，不能有二义性。在任何条件下，算法只有唯一的一条执行路径，即对相同的输入只能得出相同的结果</li><li>可行性：算法是可行的，即算法中描述的操作均可通过已经实现的基本运算的有限次执行来实现</li><li>输入：一个算法有零个或多个输入，这些输入取自算法加工对象的集合</li><li>输出：一个算法有一个或多个输出，这些输出应是算法对输入加工后合乎逻辑的结果</li></ol><p>Tips：程序和算法十分相似，但是程序不一定要满足有穷性，例如操作系统启动后，如果没有作业处理也仍会循环等待。</p><h3 id="算法的三种描述方法"><a href="#算法的三种描述方法" class="headerlink" title="算法的三种描述方法"></a>算法的三种描述方法</h3><ol><li>自然语言：不够严谨</li><li>程序流程图、N-S图等算法描述工具：简洁明了</li><li><strong>伪码语言描述</strong>：伪码语言介于高级程序设计语言和自然语言之间，它忽略高级程序设计语言中一些严格的语法规则与描述细节，因此它比程序设计语言更容易描述和被人理解，而比自然语言更接近程序设计语言</li></ol><h3 id="算法的四个衡量指标"><a href="#算法的四个衡量指标" class="headerlink" title="算法的四个衡量指标"></a>算法的四个衡量指标</h3><ol><li>正确性（硬性指标）：算法的正确性主要有4个层次的要求，第一层次是指算法没有语法错误，第二层次是指算法对于几组输入数据能够得出满足规格说明要求的结果，第三层次是指算法对于精心选择的苛刻并带有刁难性的几组输入数据能够得出满足规格说明要求的结果，第四层次是指算法对于一切合法的输入数据都能得出满足规格说明要求的结果</li><li>可读性：提高可读性的方法包含：注释、变量命名、程序缩排（代码规范）、段落（不同目的的代码块之间插入一个空白行）</li><li>健壮性：算法中应对输入数据和参数进行合法性检查，例如三角形三条边的长度</li><li>时空效率：算法的执行时间尽可能短，占用的存储空间尽可能少。但这两者往往相互矛盾，节省了时间可能牺牲空间，反之亦然。设计者应在时间与空间两方面有所平衡</li></ol><hr><h2 id="算法性能分析和度量"><a href="#算法性能分析和度量" class="headerlink" title="算法性能分析和度量"></a>算法性能分析和度量</h2><blockquote><p>可以用算法的时间复杂度与空间复杂度来评价算法的优劣</p></blockquote><h3 id="时间复杂度（Time-Complexity）（T-n-）"><a href="#时间复杂度（Time-Complexity）（T-n-）" class="headerlink" title="时间复杂度（Time Complexity）（T(n)）"></a>时间复杂度（Time Complexity）（T(n)）</h3><p>一个算法的时间复杂度是指算法运行从开始到结束所需要的时间。</p><p>这个时间就是该算法中每条语句的执行时间之和，而<code>每条语句的执行时间 = 该语句执行次数（也称为频度） * 执行该语句所需时间</code></p><p>但是，当算法转换为程序之后，一条语句执行一次所需的时间与机器的性能及编译程序生成目标代码的质量有关，是很难确定的。</p><p>为此，<strong>假设执行每条语句所需的时间均为单位时间</strong>。在这一假设下，一个算法所花费的时间就等于算法中所有语句的频度之和。这样就可以脱离机器的硬、软件环境而独立地分析算法所消耗的时间。</p><p>例如：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> N 100  <span class="comment">//问题规模</span></span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">matrixMultiply</span><span class="params">(<span class="keyword">int</span> A[N][N], <span class="keyword">int</span> B[N][N], <span class="keyword">int</span> C[N][N])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>( i=<span class="number">0</span>; i&lt;N; i=i+<span class="number">1</span>)   <span class="comment">//本身执行n+1次，但是内部的循环体是执行n次</span></span><br><span class="line">        <span class="keyword">for</span>( j=<span class="number">0</span>; j&lt;N; j=j+<span class="number">1</span>)  <span class="comment">//执行n(n+1)次，因为本身执行n+1次</span></span><br><span class="line">        &#123;</span><br><span class="line">            C[i][j] = <span class="number">0</span>;   <span class="comment">//执行n^2次</span></span><br><span class="line">            <span class="keyword">for</span>(k=<span class="number">0</span>; k&lt;N; k=k+<span class="number">1</span>)    <span class="comment">//执行(n^2)*(n+1)次</span></span><br><span class="line">                C[i][j] += A[i][k] * B[k][j];  <span class="comment">//执行n^3次</span></span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>总计执行$n+1+n(n+1)+n^2+(n^2)*(n+1)+n^3，即T(n)=2n^3+3n^2+2n+1$</p><hr><p>一般而言，<strong>一个算法的执行时间是求解问题的规模n（如矩阵的阶数、线性表的长度）的函数</strong>，这是因为问题的规模往往决定了算法工作量的大小。</p><p>但是，我们不关心它是个怎样的函数，只关心它的数量级量度，即它与什么简单函数f(n)是同一数量级的，即T(n)=O(f(n))。其中“O”是数学符号，其数学定义如下:</p><p>如果存在正的常数C和n0，使得当n≥n0时都满足0≤T(n)≤C<em>f(n)，则称T(n)与f(n)是同一数量级的，并记作T(n)=O(f(n))，即最高幂次方。*</em>算法的执行时间T(n)=O(f(n))为该算法的时间复杂度。**</p><p><code>它表示随着问题规模n的增大，该算法执行时间的增长率和f(n)的增长率相同。</code></p><p>1.对于上面的例子，当n→∞时，</p><p>$$T(n)/n^3= (2n^3+3n^2+2n+1)/ n^3→2$$</p><p>根据“O”的定义可知T(n)=O(n^3)，所以上面例子的算法的时间复杂度是<strong>O(n^3)</strong></p><p>2.例如，用两个算法A1和A2求解同一问题，它们的时间耗费分别是$T_1(n)=100n^2+5000n+3,T_2(n)=2n^3$。如果问题规模n不太大，则二者的时间花费也相差不大；若问题规模n很大，如n=10000，则二者的时间花费相差很大。二者的差别从时间复杂度上一目了然，因为$T_1(n)=O(n^2), T_2(n)=O(n^3)$，所以算法A1的时间性能优于算法A2</p><hr><p>如果一个算法的所有语句的频度之和是问题规模n的多项式，</p><p>即</p><p>$$T(n)=C_kn^k+C_{k-1}n^{k-1}+…+C_1n+C_0C_k≠0$$</p><p>则按“O”的定义可知，该算法为k次方阶算法：</p><p>$$T(n)=O(n^k)$$</p><p>特殊情况，如果T(n)=C，其中C为常数，即算法耗费的时间与问题规模n无关，则记<code>T(n)=O(1)</code>，称该算法为常数阶算法。</p><hr><h3 id="常见的时间复杂度"><a href="#常见的时间复杂度" class="headerlink" title="常见的时间复杂度"></a>常见的时间复杂度</h3><p>按数量级递增排序有：</p><ul><li>常数阶$O(1)$</li><li>对数阶$O(log_2n)$</li><li>线性阶$O(n)$</li><li>平方阶$O(n^2)$</li><li>立方阶$O(n^3)$</li><li>指数阶$O(2^n)$</li></ul><p>指数阶算法的执行时间随n的增大而迅速放大，所以其时间性能极差，当n稍大时我们就无法忍受，如汉诺塔问题。</p><p>常见的时间复杂度有：</p><p>$O(1)&lt;O(log_2n)&lt;O(n)&lt;O(nlog_2n)&lt;O(n^2)&lt;O(n^3)&lt;O(2^n)$</p><h3 id="简化时间复杂度计算"><a href="#简化时间复杂度计算" class="headerlink" title="简化时间复杂度计算"></a>简化时间复杂度计算</h3><p>由于算法的时间复杂度仅刻画了算法执行时间的数量级，为了简化对算法时间性能的分析，通常的做法是，从算法中选取一种对于所研究的问题来说是最基本的操作，并以该基本操作执行的次数作为算法的时间度量</p><p>被选用的基本操作应是其重复执行次数与算法的执行时间成正比例的</p><p>例如上面的代码例子，只需获取最大阶乘的时间复杂度，即$n^3$即可，$O(n)=n^3$。</p><hr><h3 id="平均时间复杂度"><a href="#平均时间复杂度" class="headerlink" title="平均时间复杂度"></a>平均时间复杂度</h3><h4 id="和输入数据有关的时间复杂度"><a href="#和输入数据有关的时间复杂度" class="headerlink" title="和输入数据有关的时间复杂度"></a>和输入数据有关的时间复杂度</h4><p>有些情况下，算法的时间复杂度不仅与问题的规模有关，还与输入数据有关。例如，在数组A[n]中查找给定值k的算法如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">i = n<span class="number">-1</span>;</span><br><span class="line"><span class="keyword">while</span>(i&gt;=<span class="number">0</span> &amp;&amp; A[i] != k)  =i<span class="number">-1</span>;</span><br><span class="line"><span class="keyword">return</span> i;</span><br></pre></td></tr></table></figure><p>这个操作的执行次数取决于数组A中有没有值为k的元素。若有，这个值为k的元素又在数组中哪个位置。</p><ul><li>若数组中无值为k的元素，则操作“A[i]! =k”的执行次数为n；</li><li>若A[n-1]等于k，则该操作的执行次数为1。</li></ul><p>所以，该算法在最坏情况下的时间复杂度为O(n)，最好情况下的时间复杂度是O(1)。</p><p>因此，有时我们会对算法的平均（或称期望）时间复杂度感兴趣。所谓<strong>平均时间复杂度，是指所有可能的输入数据在等概率出现的情况下算法的平均执行时间。</strong></p><p><strong>当算法的平均时间复杂度难以确定时，就以算法的最坏时间复杂度作为算法的时间复杂度</strong>。因为它是算法执行时间的一个上界，保证了算法的执行时间不会比它更长。</p><h4 id="递归算法的时间复杂度"><a href="#递归算法的时间复杂度" class="headerlink" title="递归算法的时间复杂度"></a>递归算法的时间复杂度</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">fact(<span class="keyword">int</span> n)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(n&lt;=<span class="number">1</span>)    <span class="keyword">return</span> <span class="number">1</span>;   <span class="comment">//(1)</span></span><br><span class="line">    <span class="keyword">else</span>    <span class="keyword">return</span> (n*fact(n<span class="number">-1</span>));   <span class="comment">//(2)</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>设fact(n)的运行时间复杂度函数是T(n)，该函数中语句（1）的运行时间是$O(1)$，语句（2）的运行时间是T(n-1)+O(1)，其中O(1)为基本运行时间，</p><p>因此：<br>如果n≤1，则$T(n) = O(1)$</p><p>如果n&gt;1，则$T(n) = T(n-1)+O(1)$</p><p>则$T(n) = T(n-1)+O(1)= T(n-2)+2<em>O(1)= T(n-3)+3</em>O(1)=…= T(1)+(n-1)<em>O(1)=n</em>O(1)=O(n)$</p><p>即fact(n)的时间复杂度为$O(n)$</p><hr><h3 id="空间复杂度（Space-Complexity）（S-n-）"><a href="#空间复杂度（Space-Complexity）（S-n-）" class="headerlink" title="空间复杂度（Space Complexity）（S(n)）"></a>空间复杂度（Space Complexity）（S(n)）</h3><p>空间复杂度作为算法所需存储空间的量度，记作$S(n)=O(f(n))$<br>（n为问题的规模）</p><p>度量和时间复杂度一样。</p><p>在大多数算法设计中，时间效率和空间效率很难兼得，设计者往往要根据具体问题进行取舍。</p><hr><h2 id="线性表"><a href="#线性表" class="headerlink" title="线性表"></a>线性表</h2><blockquote><p>线性表是最简单、最基本也是最常用的一种线性结构。它有两种存储方式：<strong>顺序存储方式和链式存储方式</strong>。它的主要操作是插入、删除和检索等</p></blockquote><p>逻辑结构表示为：</p><p>$LinearList = {D, R}$</p><p>其中，</p><p>$D={a_i | 1&lt;=i&lt;=n, n&gt;=0, a_i∈DataType} (DataType为数据元素类型)$</p><p>$R={r}$</p><p>$r={&lt;a_i, a_{i+1}&gt; | 1&lt;=i&lt;=n-1}$</p><p>线性表的基本运算方法如下：</p><table><thead><tr><th align="center">作用</th><th align="center">函数</th><th align="center">初始条件</th><th align="center">操作结果</th></tr></thead><tbody><tr><td align="center">线性表初始化</td><td align="center">void Initiate()</td><td align="center">线性表不存在</td><td align="center">构造一个空的线性表</td></tr><tr><td align="center">求线性表的长度</td><td align="center">int Length()</td><td align="center">线性表已存在</td><td align="center">返回线性表所含数据元素的个数</td></tr><tr><td align="center">取表元</td><td align="center">DataType Get(int i)</td><td align="center">表存在且1≤i≤Length()</td><td align="center">返回线性表的第i个数据元素的值</td></tr><tr><td align="center">按值查找</td><td align="center">int Locate(DataType x)</td><td align="center">线性表已存在，x是给定的一个数据元素</td><td align="center">查找值为x的数据元素，返回首次出现的值为x的那个数据元素的序号，称为查找成功；如果未找到值为x的数据元素，返回0表示查找失败</td></tr><tr><td align="center">插入操作</td><td align="center">int Insert(DataType x, int i)</td><td align="center">线性表已存在</td><td align="center">在线性表的第i个位置上插入一个值为x的新元素，使原序号为i,i+1, …,n的数据元素的序号变为i+1,i+2, …,n+1，插入后表长=原表长+1，返回1表示插入成功；若线性表L中数据元素个数少于i-1个，则返回0表示插入失败</td></tr><tr><td align="center">删除操作</td><td align="center">int Deleted(int i)</td><td align="center">线性表已存在</td><td align="center">在线性表L中删除序号为i的数据元素，删除后使序号为i+1, i+2, …, n的元素变为序号i, i+1, …, n-1，新表长=原表长-1，返回1；若线性表中数据元素个数少于i，则返回0表示删除失败</td></tr></tbody></table><hr><h3 id="顺序存储结构-顺序表"><a href="#顺序存储结构-顺序表" class="headerlink" title="顺序存储结构-顺序表"></a>顺序存储结构-顺序表</h3><p>内存中的地址空间是线性的，顺序表存储数据时，会提前申请一整块足够大小的物理空间，然后将数据依次存储起来，存储时做到数据元素之间不留一丝缝隙</p><p>设数据元素a1的存储地址为Loc(a1)，每个数据元素占用d个存储地址，则第i个数据元素的地址为：</p><p>$Loc(a_i)=Loc(a_1)+(i-1)*d (其中1≤i≤n)$</p><p><font color=green><strong>特点</strong>：只要知道顺序表的首地址和每个数据元素所占用地址单元的个数，就可以求出第i个数据元素的地址。这也是顺序表具有按数据元素的序号随机存取的特点。</font></p><hr><p>因为线性表的运算经常会导致长度发生变化，所以数组容量需要设计的足够大</p><p>假设用$data[MAXSIZE]$来表示，其中MAXSIZE是一个根据实际问题定义的足够大的整数，线性表中的数据元素从data[0]开始依次存放，但当前线性表中的实际元素个数可能未达到MAXSIZE，因此需要用一个<strong>变量len</strong>记录当前线性表中数据元素个数，使得len起到一个数字指针的作用，始终指向线性表中最后一个元素的下一个位置，其值为最后一个数据元素的位置值，若该表为空表，则len=0。</p><h4 id="顺序表运算"><a href="#顺序表运算" class="headerlink" title="顺序表运算"></a>顺序表运算</h4><ol><li>初始化</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//初始化</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SequenList::Initiate</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  len=<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">void</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  SequenList L;</span><br><span class="line">  L.Initiate();</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>插入</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在线性表的第i个元素之前插入一个新的数据元素x</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SequenList::Insert</span><span class="params">(DataType x, <span class="keyword">int</span> i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="comment">//做插入i位置的判断，是否溢出(MAXSIZE)或者不合法(len)</span></span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">int</span> j=len; j&gt;=i; j--)</span><br><span class="line">      data[j]=data[j<span class="number">-1</span>];   <span class="comment">//元素后移</span></span><br><span class="line"></span><br><span class="line">  data[i<span class="number">-1</span>]=x; <span class="comment">//插入元素</span></span><br><span class="line">  len++;  <span class="comment">//表长度+1</span></span><br><span class="line">  <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>插入算法的时间性能分析</strong>：顺序表上的插入运算，时间主要消耗在数据元素的移动上。在第i个位置上插入x，从第i个到第n个元素都要向后移动一个位置，共需要移动n-(i-1)，即n-i+1个数据元素。而i的取值范围为1≤i≤n+1，即有n+1个位置可以插入</p><p><font color=green>在顺序表上做插入操作需移动表中一半数据元素。时间复杂度为O(n)</font></p><ol start="3"><li>删除</li></ol><p>在顺序表上完成删除运算的步骤如下：</p><p>（1）将数据元素ai+1～an依次向前移动一个位置</p><p>（2）修改len值，使之仍指向最后一个数据元素的下一个位置</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//删除第i个元素</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SequenList::Delete</span><span class="params">(<span class="keyword">int</span> i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="comment">//做插入i位置的判断，是否溢出(MAXSIZE)或者不合法(len)</span></span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">int</span> j=i; j&lt;len; j++)</span><br><span class="line">      data[j<span class="number">-1</span>]=data[j];   <span class="comment">//元素前移</span></span><br><span class="line"></span><br><span class="line">  len--;  <span class="comment">//表长度-1</span></span><br><span class="line">  <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>删除算法的时间性能分析</strong>：与插入运算相同，其时间主要消耗在移动表中元素上。删除第i个元素时，其后面的元素$a_{i+1}-a_n$都要向前移动一个位置，共移动n-i个元素</p><p><font color=green>在顺序表上做删除运算时大约需要移动表中一半元素。该算法的时间复杂度为O(n)</font></p><ol start="4"><li>按值查找</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SequenList::Locate</span><span class="params">(DataType x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="comment">//返回值为x的元素的位序值</span></span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">int</span> j=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span>((j&lt;len)&amp;&amp;(data[j] != x)) j++;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(j&lt;len) <span class="keyword">return</span> j+<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">else</span> <span class="keyword">return</span> <span class="number">0</span>;  <span class="comment">//没有查找到</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>比较的次数与x在表中的位置有关，也与表长有关。当a1=x时，比较一次成功；当an=x时，比较n次成功。</p><p><font color=green>在查找成功的情况下，平均比较次数为（n+1）/2，时间复杂度为O(n)</font></p><ol start="5"><li>读取第i个数据元素的值</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">SequenList::Get</span><span class="params">(<span class="keyword">int</span> i)</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>((i&lt;<span class="number">1</span>) || (i&gt;len))</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt;<span class="string">"not correct input"</span>&lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="keyword">return</span> data[i<span class="number">-1</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="6"><li>取得元素个数</li></ol><p>直接return len值</p><hr><h4 id="顺序表存储空间的分配"><a href="#顺序表存储空间的分配" class="headerlink" title="顺序表存储空间的分配"></a>顺序表存储空间的分配</h4><p>线性表顺序存储结构，是预先给定大小为MAXSIZE的存储空间，程序在编译阶段就已经知道该类型变量的大小，在程序开始运行前会为它分配好存储空间，因此是一种<strong>存储空间的静态分配</strong></p><p>而动态分配是在定义线性表的存储类型时，不是定义好一个存储空间，而是只定义一个指针，待程序运行后再申请一个用于存放线性表数据元素的存储空间，并把该存储空间的起始地址赋给这个指针。访问动态存储分配的线性表中的元素和访问静态存储分配的线性表中的元素的情况完全相同，既可以采用指针方式，也可以采用数组下标方式。</p><p>关于数据内存存储的方式可参考<a href="https://wizzie.top/Blog/2019/08/13/2019/190813_cpp_typeTemplete/#%E7%AE%A1%E7%90%86%E6%95%B0%E6%8D%AE%E5%86%85%E5%AD%98%E7%9A%84%E5%9B%9B%E7%A7%8D%E6%96%B9%E5%BC%8F" target="_blank" rel="noopener">C++内存分配方式-管理数据内存的四种方式</a></p><hr><h3 id="链式存储结构"><a href="#链式存储结构" class="headerlink" title="链式存储结构"></a>链式存储结构</h3><p>顺序表的存储特点是用物理位置上的相邻实现了逻辑关系上的相邻，它要求用连续的存储单元顺序存储线性表中的各元素，因此，对顺序表插入、删除操作时需要通过移动数据元素来实现，严重影响了运行效率</p><p>而链式存储结构不需要用地址连续的存储单元来实现，因为它不要求逻辑关系上相邻的两个数据元素物理位置上也相邻。它通过“链”建立起数据元素之间的逻辑关系。因此对线性表的插入、删除不需要移动数据元素</p><h4 id="1-单链表结构"><a href="#1-单链表结构" class="headerlink" title="1.单链表结构"></a>1.单链表结构</h4><blockquote><p>链表是通过一组任意的存储单元来存储线性表中的数据元素</p></blockquote><p><font color=green>为建立起数据元素之间的线性关系，对每个数据元素ai，除了存放数据元素自身的数据信息ai之外，还需要存放其后继ai+1所在的存储单元的地址，这两部分信息组成一个“结点”。</p><p>存放数据元素信息的域称为数据域，存放其后继数据元素地址的域称为指针域</p><p>n个数据元素的线性表通过每个结点的指针域形成了一个“链”，称为链表。</p><p>由于每个结点中只有一个指向后继的指针，所以称其为单链表</font></p><p>结点结构：</p><p><img src="link_1.png" alt="结点结构"></p><ul><li>头指针：一个指向第一个节点地址的指针变量，头指针具有标识单链表的作用，所以经常用头指针代表单链表的名字</li><li>头结点：在单链表的第一个结点之前附设一个结点，它没有直接前驱，称之为头结点<br>可不存信息，也可以作为监视哨，或用于存放线性表的长度等附加信息<br>指针域中存放首元结点的地址</li><li>首元结点：存储第一个元素的节点</li></ul><p>定义及基本运算函数：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">int</span> DataType;</span><br><span class="line">clasee Item</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">public</span>:</span><br><span class="line">    DataType data;</span><br><span class="line">    Item *next;</span><br><span class="line">    Item() &#123;next=<span class="literal">NULL</span>&#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Link</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="keyword">public</span>:</span><br><span class="line">  Item *head; <span class="comment">//链表头指针</span></span><br><span class="line">  Link() &#123;head=<span class="literal">NULL</span>;&#125;  <span class="comment">//构造函数</span></span><br><span class="line">  ~Link() &#123;DeleteAll(); &#125; <span class="comment">//析构函数</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">Initiate</span><span class="params">()</span></span>; <span class="comment">//初始化</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">DeleteAll</span><span class="params">()</span></span>;   <span class="comment">//删除所有结点</span></span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">HeadCreate</span><span class="params">(<span class="keyword">int</span> n)</span></span>;  <span class="comment">//从头建链表</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">TailCreate</span><span class="params">(<span class="keyword">int</span> n)</span></span>; <span class="comment">//从尾建链表</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">HeadCreateWithHead</span><span class="params">(<span class="keyword">int</span> n)</span></span>; <span class="comment">//建立带表头的链表（从头）</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">TailreateWithHead</span><span class="params">(<span class="keyword">int</span> n)</span></span>; <span class="comment">//建立带表头的链表（从尾）</span></span><br><span class="line">  <span class="function"><span class="keyword">int</span> <span class="title">Length</span><span class="params">()</span></span>;  <span class="comment">//链表长度</span></span><br><span class="line"></span><br><span class="line">  <span class="function">Item *<span class="title">Locatex</span><span class="params">(DataType x)</span></span>;  <span class="comment">//查找值为x的数据元素</span></span><br><span class="line">  <span class="function">Item *<span class="title">Locatei</span><span class="params">(<span class="keyword">int</span> i)</span></span>;   <span class="comment">//查找第i个元素</span></span><br><span class="line">  <span class="function">DataType <span class="title">Get</span><span class="params">(<span class="keyword">int</span> i)</span></span>;    <span class="comment">//获取第i个元素的值</span></span><br><span class="line">  <span class="function"><span class="keyword">bool</span> <span class="title">Insert</span><span class="params">(DataType x, <span class="keyword">int</span> i)</span></span>;  <span class="comment">//在链表第i个结点之前插入x</span></span><br><span class="line">  <span class="function"><span class="keyword">bool</span> <span class="title">Deleted</span><span class="params">(<span class="keyword">int</span> i)</span></span>;  <span class="comment">//删除链表中第i个结点</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">Print</span><span class="params">()</span></span>;   <span class="comment">//打印链表</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="单链表运算-初始化"><a href="#单链表运算-初始化" class="headerlink" title="单链表运算-初始化"></a>单链表运算-初始化</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Initiate</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  DeleteAll();</span><br><span class="line">  head=<span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="单链表运算-建立单链表"><a href="#单链表运算-建立单链表" class="headerlink" title="单链表运算-建立单链表"></a>单链表运算-建立单链表</h4><p>链表与顺序表不同，它是一种动态管理的存储结构，链表中的每个结点占用的存储空间不是预先分配的，而是在运行时系统根据需求动态生成的，因此建立单链表应该从空表开始。</p><p><font color=red>(1)从表尾到表头建立单链表（不带有空白头结点）</font></p><p>可以在每读入一个数据元素后申请一个结点，然后插在链表的头部（从表尾到表头建立单链表）</p><p>因为是在链表的头部插入，读入数据的顺序和线性表中的逻辑顺序是相反的。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Link::HeadCreate</span><span class="params">(<span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  DeleteAll();</span><br><span class="line">  Item *s, *p;</span><br><span class="line">  <span class="keyword">int</span> i;</span><br><span class="line">  p=<span class="literal">NULL</span>;</span><br><span class="line">  <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=n; i++)</span><br><span class="line">  &#123;</span><br><span class="line">    s=<span class="keyword">new</span> Item();</span><br><span class="line">    <span class="built_in">cin</span>&gt;&gt; s-&gt;data;   <span class="comment">//输入</span></span><br><span class="line">    s-&gt;next = p;</span><br><span class="line">    p = s;</span><br><span class="line">  &#125;</span><br><span class="line">  head = p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><font color=red>(2)从表头到表尾建立单链表（不带有空白头结点）</font></p><p>从表尾到表头插入结点建立单链表比较简单，但读入的数据元素的顺序与生成的单链表中元素的顺序是相反的。</p><p>若希望次序一致，则用从表头到表尾建立单链表的方法。</p><p>因为每次是将新结点插入链表的尾部，所以需加入一个指针r用来始终指向链表中的尾结点，以便能够将新结点插入链表的尾部</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Link::TailCreate</span><span class="params">(<span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  DeleteAll();</span><br><span class="line">  Item *s, *r, *p;</span><br><span class="line">  <span class="keyword">int</span> i;</span><br><span class="line">  p=<span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=n; i++)</span><br><span class="line">  &#123;</span><br><span class="line">    s=<span class="keyword">new</span> Item();</span><br><span class="line">    <span class="built_in">cin</span>&gt;&gt; s-&gt;data;   <span class="comment">//输入</span></span><br><span class="line">    s-&gt;next = <span class="literal">NULL</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">//它的地址就是整个链表的指针，需要放在链表的头指针变量p中</span></span><br><span class="line">    <span class="keyword">if</span>(p==<span class="literal">NULL</span>) p=r=s;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">      r-&gt;next=s;  <span class="comment">//此处的顺序和上面那种方式相反</span></span><br><span class="line">      r=s;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  head = p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><font color=red>(3)从表尾到表头建立单链表（带有空白头结点）</font></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Link::TailCreate</span><span class="params">(<span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  DeleteAll();</span><br><span class="line">  Item *s, *p;</span><br><span class="line">  <span class="keyword">int</span> i;</span><br><span class="line">  p=<span class="keyword">new</span> Item();</span><br><span class="line">  p-&gt;next=<span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=n; i++)</span><br><span class="line">  &#123;</span><br><span class="line">    s=<span class="keyword">new</span> Item();</span><br><span class="line">    <span class="built_in">cin</span>&gt;&gt; s-&gt;data;   <span class="comment">//输入</span></span><br><span class="line">    s-&gt;next = p-&gt;next;  <span class="comment">//带有空白头结点</span></span><br><span class="line">    p-&gt;next = s;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  head = p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><font color=red>(4)从表头到表尾建立单链表（带有空白头结点）</font></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Link::TailCreate</span><span class="params">(<span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  DeleteAll();</span><br><span class="line">  Item *s, *r, *p;</span><br><span class="line">  <span class="keyword">int</span> i;</span><br><span class="line">  p=<span class="keyword">new</span> Item();</span><br><span class="line">  p-&gt;next=<span class="literal">NULL</span>;</span><br><span class="line">  r=p;   <span class="comment">//带有空白头结点</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=n; i++)</span><br><span class="line">  &#123;</span><br><span class="line">    s=<span class="keyword">new</span> Item();</span><br><span class="line">    <span class="built_in">cin</span>&gt;&gt; s-&gt;data;   <span class="comment">//输入</span></span><br><span class="line">    r-&gt;next=s;</span><br><span class="line">    r=s;</span><br><span class="line">  &#125;</span><br><span class="line">  r-&gt;next=<span class="literal">NULL</span>;</span><br><span class="line">  head = p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从上面4个算法可以看出，对于不带空白头结点的单链表，空表情况下需要单独处理，而带上空白头结点之后则不用了，<strong>算法的时间复杂度均为O(n)</strong></p><hr><h4 id="单链表运算-求表长"><a href="#单链表运算-求表长" class="headerlink" title="单链表运算-求表长"></a>单链表运算-求表长</h4><p>设有一个移动指针p和计数器j，初始化后，p所指结点后面若还有结点，p向后移动，计数器加1。算法如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">Link::Length</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> j=<span class="number">1</span>;</span><br><span class="line">  Item *p;</span><br><span class="line">  p=head-&gt;next;</span><br><span class="line">  <span class="keyword">while</span>(p!=<span class="literal">NULL</span>)</span><br><span class="line">  &#123;</span><br><span class="line">    j++;</span><br><span class="line">    p=p-&gt;next;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> --j;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>此算法的时间复杂度为O(n)</strong></p><hr><h4 id="单链表运算-查找操作"><a href="#单链表运算-查找操作" class="headerlink" title="单链表运算-查找操作"></a>单链表运算-查找操作</h4><p>(1)按序号查找</p><p>从单链表的第一个元素结点起，判断当前结点是否是第i个，若是，则返回该结点的指针；否则继续下一个结点的查找，直到表结束为止。</p><p>若没有第i个结点，则返回空；如果i=0，则返回头指针。算法如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Item *<span class="title">Locatei</span><span class="params">(<span class="keyword">int</span> i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> j=<span class="number">1</span>;</span><br><span class="line">  Item *p;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(i=<span class="number">1</span>) <span class="keyword">return</span> head;</span><br><span class="line"></span><br><span class="line">  p=head-&gt;next;</span><br><span class="line">  <span class="keyword">while</span>((p!=<span class="literal">NULL</span>) &amp;&amp; (j&lt;i))</span><br><span class="line">  &#123;</span><br><span class="line">    j++;</span><br><span class="line">    p=p-&gt;next;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(j==i) <span class="keyword">return</span> p;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">cout</span>&lt;&lt;<span class="string">"not correct input!"</span> &lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>时间复杂度均为O(n)</strong></p><p>(2)按值查找即定位</p><p>从链表的第一个元素结点起，判断当前结点值是否等于x，若是，返回该结点的指针，否则继续下一个结点的查找，直到表结束为止。若找不到，则返回空。算法如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Item *<span class="title">Locatex</span><span class="params">(DataType x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  Item *p;</span><br><span class="line">  p=head-&gt;next;</span><br><span class="line">  <span class="keyword">while</span>((p!=<span class="literal">NULL</span>) &amp;&amp; (p-&gt;data != x))  p=p-&gt;next;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(p)   <span class="keyword">return</span> p;   <span class="comment">//即p不为空</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">cout</span>&lt;&lt; x &lt;&lt; <span class="string">" is not exist"</span> &lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>时间复杂度均为O(n)</strong></p><p>(3) 读取第i个位置上的元素值</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">DataType <span class="title">Link::Get</span><span class="params">(<span class="keyword">int</span> i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> j;</span><br><span class="line">  Item *p;</span><br><span class="line">  j=<span class="number">1</span>;</span><br><span class="line">  p=head-&gt;next;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span>((j&lt;i) &amp;&amp; (p != <span class="literal">NULL</span>))</span><br><span class="line">  &#123;</span><br><span class="line">    j++;</span><br><span class="line">    p=p-&gt;next;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>((p==<span class="literal">NULL</span>) || (j&gt;i))</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt;<span class="string">"not correct"</span> &lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">return</span> p-&gt;data;  </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>时间复杂度均为O(n)</strong></p><hr><h4 id="单链表运算-插入"><a href="#单链表运算-插入" class="headerlink" title="单链表运算-插入"></a>单链表运算-插入</h4><p>(1)后插结点</p><p>设p指向单链表中某结点，s指向待插入的值为x的新结点，将*s插入 *p的后面</p><p>操作如下：（两个指针的操作顺序不能交换）</p><p>① s-&gt;next=p-&gt;next;</p><p>② p-&gt;next=s;</p><p><strong>时间复杂度为O(1)</strong></p><hr><p>(2)前插结点</p><p>设p指向链表中某结点，s指向待插入的值为x的新结点，将*s插入 *p的前面</p><p>与后插不同的是，首先要找到＊p的前驱＊q，再完成在<em>q之后插入</em>s。设单链表头指针为L，操作如下：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">q=L;</span><br><span class="line"><span class="keyword">while</span>(q-&gt;next != p)</span><br><span class="line">  q=q-&gt;next;  <span class="comment">//找到*p的直接前驱</span></span><br><span class="line"></span><br><span class="line">s-&gt;next = q-&gt;next;</span><br><span class="line">q-&gt;next = s;</span><br></pre></td></tr></table></figure><p><strong>时间复杂度为O(n)</strong></p><p>如果将*s直接插入 *p的后面，然后将p-&gt;data与s-&gt;data交换。这样既满足了逻辑关系，也能使得时间复杂度为O(1)。</p><hr><p>(3)插入算法</p><p>步骤：</p><p>① 找到第i-1个结点；若存在，继续步骤②，否则结束</p><p>② 申请新结点，将数据填入新结点的数据域</p><p>③ 将新结点插入</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">Link::Insert</span><span class="params">(DataType x, <span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">  Item *p, *s;</span><br><span class="line">  p = Locatei(i<span class="number">-1</span>);  <span class="comment">//查找i-1的结点</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(p == <span class="literal">NULL</span>) &#123;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"not correct"</span> &lt;&lt;<span class="built_in">endl</span>;</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">  s = <span class="keyword">new</span> Item();</span><br><span class="line">  s-&gt;data = x;</span><br><span class="line">  s-&gt;next = p-&gt;next;</span><br><span class="line">  p-&gt;next = s;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h4 id="单链表运算-删除"><a href="#单链表运算-删除" class="headerlink" title="单链表运算-删除"></a>单链表运算-删除</h4><p>(1)删除结点</p><p>设p指向单链表中某结点，删除*p</p><p>要实现对结点*p的删除，</p><p>首先要找到*p的前驱结点 *q，然后完成指针的删除操作即可</p><p>指针的操作，由下列语句实现：</p><figure class="highlight cpp"><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">q-&gt;next = p-&gt;next;</span><br><span class="line"><span class="keyword">delete</span> p;</span><br></pre></td></tr></table></figure><p>PS:找前驱结点*q的时间复杂度是$O(n)$</p><p>删除：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">s = p-&gt;next;</span><br><span class="line">p-&gt;next = s-&gt;next;</span><br><span class="line"><span class="keyword">delete</span> s;</span><br></pre></td></tr></table></figure><p>PS:该操作时间复杂度$O(1)$</p><hr><p>(2)删除运算</p><p>① 找到第i-1个结点，若存在，继续步骤②，否则结束</p><p>② 若存在第i个结点，则继续步骤③，否则结束</p><p>③ 删除第i个结点，结束</p><hr><h4 id="单链表运算-删除所有结点"><a href="#单链表运算-删除所有结点" class="headerlink" title="单链表运算-删除所有结点"></a>单链表运算-删除所有结点</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Link::DeleteAll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  Item *p = head, *q;</span><br><span class="line">  <span class="keyword">while</span>(p != <span class="literal">NULL</span>) &#123;</span><br><span class="line">    q = p-&gt;next;</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = q;</span><br><span class="line">  &#125;</span><br><span class="line">  head = <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过上面的基本操作可知：</p><ol><li>在单链表上插入、删除一个结点，必须知道其前驱结点的指针；</li><li>单链表不具有按序号随机访问的特点，只能从头指针开始一个个顺次进行。</li></ol><hr><h4 id="2-循环链表结构"><a href="#2-循环链表结构" class="headerlink" title="2.循环链表结构"></a>2.循环链表结构</h4><p>对于单链表而言，最后一个结点的指针域是空指针。</p><p>如果将该链表头指针置入该指针域，则使得链表头尾结点相连，就构成了循环单链表</p><p><strong>对于单链表，只能从头结点开始遍历整个链表；而对于循环单链表，则可以从表中任意结点开始遍历整个链表。</strong>不仅如此，有时对链表常做的操作是在表尾、表头之间进行。</p><p><strong>此时可以改变链表的标识方法，不用头指针而用一个指向尾结点的指针R来标识，可以使操作效率得以提高</strong></p><hr><h4 id="3-双向链表结构"><a href="#3-双向链表结构" class="headerlink" title="3.双向链表结构"></a>3.双向链表结构</h4><p>单链表的结点中只有一个指向其后继结点的指针域next，因此若已知某结点的指针为p，其后继结点的指针则为p-&gt;next，而找其前驱则只能从该链表的头指针开始，顺着各结点的next域进行。</p><p>也就是说，找后继的时间复杂度是O(1)，找前驱的时间复杂度是O(n)。</p><p><strong>如果也希望找前驱的时间复杂度达到O(1)，则只能付出空间的代价：每个结点再加一个指向前驱的指针域，用这种结点组成的链表称为双向链表</strong></p><hr><h3 id="顺序表和链式表对比"><a href="#顺序表和链式表对比" class="headerlink" title="顺序表和链式表对比"></a>顺序表和链式表对比</h3><p>顺序存储有以下3个优点：</p><ol><li>方法简单，各种高级语言中都有数组，容易实现</li><li>不用为表示结点间的逻辑关系而增加额外的存储开销</li><li>可以按元素序号随机访问表中结点</li></ol><p>缺点：</p><ol><li>在顺序表中做插入、删除操作时，平均约需移动表中一半元素。因此对数据元素较多的顺序表来说，运算效率比较低</li><li>需要预先分配足够大的存储空间，估计得过大，可能会导致顺序表空间大量闲置；<strong>预先分配过小，容易会造成溢出</strong></li></ol><p>链式表优点：</p><ol><li>基于存储的考虑，顺序表的存储空间是静态分配的，在程序执行之前必须明确规定它的存储规模。对线性表的长度或存储规模难以估计时，不宜采用顺序表。<strong>链式表不用事先估计存储规模，但链式表的存储密度较低</strong></li><li>基于运算的考虑，在顺序表中按序号访问ai的时间复杂度是O(1)，而在链式表中按序号访问ai的时间复杂度是O(n)，所以<strong>如果经常做的操作是按序号访问数据元素，显然顺序表优于链式表</strong>。在顺序表中做插入、删除时，平均约需移动表中一半元素，当数据元素的信息量较大且表较长时；在链式表中做插入、删除时，虽然也要找插入位置，但主要是比较操作。从这个角度考虑，显然后者优于前者</li><li>基于难易的考虑，顺序表容易实现，任何高级语言中都有数组类型；链式表的操作是基于指针的</li></ol><p>PS：存储密度，是指一个结点中数据元素所占的存储单元和整个结点所占的存储单元之比。显然，链式存储结构的存储密度小于1。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://blog.csdn.net/syzdev/article/details/77888024" target="_blank" rel="noopener">C语言-链表的创建头插法和尾插法（有无头节点）</a></li><li><a href="https://zhuanlan.zhihu.com/p/84950700" target="_blank" rel="noopener">如何使用C++实现单链表</a></li><li><a href="https://zhuanlan.zhihu.com/p/83617598" target="_blank" rel="noopener">C语言之单链表操作</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;《数据结构（C++语言版）》书，关于数据结构再读记录。主要包含数据结构的基本概念、算法概念、时间复杂度、线性表（顺序表&amp;amp;链表）&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/categories/cpp/"/>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/tags/cpp/"/>
    
      <category term="datastructure" scheme="https://alonealive.github.io/Blog/tags/datastructure/"/>
    
  </entry>
  
  <entry>
    <title>Android R背光调节流程梳理</title>
    <link href="https://alonealive.github.io/Blog/2021/05/07/2021/210507_android_brightness/"/>
    <id>https://alonealive.github.io/Blog/2021/05/07/2021/210507_android_brightness/</id>
    <published>2021-05-07T14:32:00.000Z</published>
    <updated>2021-05-07T09:08:29.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>参考Android R源码，对背光手动调节、自动调节的流程作简单梳理。</p></blockquote><a id="more"></a><h2 id="SystemUI-settings手动背光调节"><a href="#SystemUI-settings手动背光调节" class="headerlink" title="SystemUI-settings手动背光调节"></a>SystemUI-settings手动背光调节</h2><h3 id="函数调用流程"><a href="#函数调用流程" class="headerlink" title="函数调用流程"></a>函数调用流程</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">frameworks&#x2F;base&#x2F;packages&#x2F;SystemUI&#x2F;src&#x2F;com&#x2F;android&#x2F;systemui&#x2F;settings&#x2F;BrightnessController.java - onChanged  ----&gt;</span><br><span class="line">setBrightness  ----&gt;</span><br><span class="line">frameworks&#x2F;base&#x2F;core&#x2F;java&#x2F;android&#x2F;hardware&#x2F;display&#x2F;DisplayManager.java - setTemporaryBrightness ----&gt;</span><br><span class="line">frameworks&#x2F;base&#x2F;core&#x2F;java&#x2F;android&#x2F;hardware&#x2F;display&#x2F;DisplayManagerGlobal.java - setTemporaryBrightness [调用IDisplayManager.aidl的setTemporaryBrightness]---&gt;</span><br><span class="line">frameworks&#x2F;base&#x2F;services&#x2F;core&#x2F;java&#x2F;com&#x2F;android&#x2F;server&#x2F;display&#x2F;DisplayManagerService.java - setTemporaryBrightness [binder call跨进程 APP进程到DMS进程]</span><br><span class="line">frameworks&#x2F;base&#x2F;services&#x2F;core&#x2F;java&#x2F;com&#x2F;android&#x2F;server&#x2F;display&#x2F;DisplayPowerController.java - setTemporaryBrightness [消息处理]  ----&gt;</span><br><span class="line">updatePowerState* [主要设置、实现背光函数]</span><br></pre></td></tr></table></figure><p><img src="setBrightness.png" alt="调用流程图"></p><h3 id="部分代码梳理"><a href="#部分代码梳理" class="headerlink" title="部分代码梳理"></a>部分代码梳理</h3><ol><li>BrightnessController.java设置背光：</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//frameworks/base/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.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">onChanged</span><span class="params">(ToggleSlider toggleSlider, <span class="keyword">boolean</span> tracking, <span class="keyword">boolean</span> automatic,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">int</span> value, <span class="keyword">boolean</span> stopTracking)</span> </span>&#123;</span><br><span class="line">        ....</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> minBacklight;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> maxBacklight;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> metric;</span><br><span class="line">        <span class="keyword">final</span> String settingToChange;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取背光值</span></span><br><span class="line">        <span class="comment">//value入参，Gamma转换成线性</span></span><br><span class="line">        <span class="comment">//valFloat:根据亮度条的拖动，计算出新的亮度值</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> valFloat = MathUtils.min(convertGammaToLinearFloat(value,</span><br><span class="line">                minBacklight, maxBacklight),</span><br><span class="line">                <span class="number">1.0f</span>);</span><br><span class="line">        ....</span><br><span class="line">        <span class="comment">//设置背光值</span></span><br><span class="line">        setBrightness(valFloat);</span><br><span class="line">        <span class="keyword">if</span> (!tracking) &#123;</span><br><span class="line">            <span class="comment">//在异步任务中将新的亮度值保存在SettingsProvider中</span></span><br><span class="line">            AsyncTask.execute(<span class="keyword">new</span> Runnable() &#123;</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">                        Settings.System.putFloatForUser(mContext.getContentResolver(),</span><br><span class="line">                                settingToChange, valFloat, UserHandle.USER_CURRENT);</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">for</span> (BrightnessStateChangeCallback cb : mChangeCallbacks) &#123;</span><br><span class="line">            cb.onBrightnessLevelChanged();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//设置背光值</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">setBrightness</span><span class="params">(<span class="keyword">float</span> brightness)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//DisplayManagerService对象</span></span><br><span class="line">        mDisplayManager.setTemporaryBrightness(brightness);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//frameworks/base/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java</span></span><br><span class="line">    <span class="comment">// Hybrid Log Gamma constant values</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> R = <span class="number">0.5f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> A = <span class="number">0.17883277f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> B = <span class="number">0.28466892f</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> C = <span class="number">0.55991073f</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">final</span> <span class="keyword">float</span> <span class="title">convertGammaToLinearFloat</span><span class="params">(<span class="keyword">int</span> val, <span class="keyword">float</span> min, <span class="keyword">float</span> max)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 计算 (val-GAMMA_SPACE_MIN) / (GAMMA_SPACE_MAX-GAMMA_SPACE_MAX) </span></span><br><span class="line">        <span class="comment">//最大值减去最小值评价一组数据的离散度，即极差</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> normalizedVal = MathUtils.norm(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, val); <span class="comment">//min 0, max 65535</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> ret;</span><br><span class="line">        <span class="comment">//对调用者自身的rgb值平方或开放</span></span><br><span class="line">        <span class="keyword">if</span> (normalizedVal &lt;= R) &#123;</span><br><span class="line">            ret = MathUtils.sq(normalizedVal / R); <span class="comment">//开方根</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            ret = MathUtils.exp((normalizedVal - C) / A) + B;  <span class="comment">//以自然常数e为底的指数函数</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// HLG is normalized to the range [0, 12], ensure that value is within that range,</span></span><br><span class="line">        <span class="comment">// it shouldn't be out of bounds.</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> normalizedRet = MathUtils.constrain(ret, <span class="number">0</span>, <span class="number">12</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Re-normalize to the range [0, 1]</span></span><br><span class="line">        <span class="comment">// in order to derive the correct setting value.</span></span><br><span class="line">        <span class="keyword">return</span> MathUtils.lerp(min, max, normalizedRet / <span class="number">12</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>AIDL跨进程（bind call）到DMS.java</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java</span></span><br><span class="line">    <span class="meta">@VisibleForTesting</span></span><br><span class="line">    <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">BinderService</span> <span class="keyword">extends</span> <span class="title">IDisplayManager</span>.<span class="title">Stub</span> </span>&#123;</span><br><span class="line">        .....</span><br><span class="line">        <span class="meta">@Override</span> <span class="comment">// Binder call</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTemporaryBrightness</span><span class="params">(<span class="keyword">float</span> brightness)</span> </span>&#123;</span><br><span class="line">            mContext.enforceCallingOrSelfPermission(</span><br><span class="line">                    Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,</span><br><span class="line">                    <span class="string">"Permission required to set the display's brightness"</span>);</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">long</span> token = Binder.clearCallingIdentity();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//同步执行代码块</span></span><br><span class="line">                <span class="comment">//保证在同一时刻最多只有一个线程执行该段代码</span></span><br><span class="line">                <span class="keyword">synchronized</span> (mSyncRoot) &#123;</span><br><span class="line">                    mDisplayPowerController.setTemporaryBrightness(brightness);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                Binder.restoreCallingIdentity(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">//frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTemporaryBrightness</span><span class="params">(<span class="keyword">float</span> brightness)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//mHandler是DisplayControllerHandler类型</span></span><br><span class="line">        <span class="comment">//创建message（省去了创建对象申请内存的开销，相比new message）</span></span><br><span class="line">        Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,</span><br><span class="line">                Float.floatToIntBits(brightness), <span class="number">0</span> <span class="comment">/*unused*/</span>);</span><br><span class="line">        <span class="comment">//内部调用sendMessage，即发送消息</span></span><br><span class="line">        msg.sendToTarget();</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> <span class="class"><span class="keyword">class</span> <span class="title">DisplayControllerHandler</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">DisplayControllerHandler</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 class="comment">/*async*/</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><br><span class="line">                <span class="comment">//消息处理</span></span><br><span class="line">                <span class="keyword">case</span> MSG_SET_TEMPORARY_BRIGHTNESS:</span><br><span class="line">                    <span class="comment">//入参int，返回float类型，赋值给mTemporaryScreenBrightness</span></span><br><span class="line">                    mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1);</span><br><span class="line">                    updatePowerState();</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                ...</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></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">updatePowerState</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Update the power state request.</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> mustNotify;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> previousPolicy;</span><br><span class="line">        <span class="keyword">boolean</span> mustInitialize = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">int</span> brightnessAdjustmentFlags = <span class="number">0</span>;</span><br><span class="line">        mBrightnessReasonTemp.set(<span class="keyword">null</span>);</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">// 如果power状态变化，则第一时间初始化更新</span></span><br><span class="line">        <span class="keyword">if</span> (mustInitialize) &#123;</span><br><span class="line">            initialize();</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">//获取p-sensor距离感应器（Apply the proximity sensor）</span></span><br><span class="line">        <span class="keyword">if</span> (mProximitySensor != <span class="keyword">null</span>) &#123;</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//1. 手动设置亮度是否改变</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> userSetBrightnessChanged = updateUserSetScreenBrightness();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果没有覆盖，则使用手动设置的临时背光</span></span><br><span class="line">        <span class="keyword">if</span> (isValidBrightnessValue(mTemporaryScreenBrightness)) &#123;</span><br><span class="line">            <span class="comment">//2. 将手动设置的背光值 赋值给 brightnessState</span></span><br><span class="line">            brightnessState = mTemporaryScreenBrightness;</span><br><span class="line">            mAppliedTemporaryBrightness = <span class="keyword">true</span>;</span><br><span class="line">            mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mAppliedTemporaryBrightness = <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();</span><br><span class="line">        <span class="keyword">if</span> (autoBrightnessAdjustmentChanged) &#123;</span><br><span class="line">            mTemporaryAutoBrightnessAdjustment = Float.NaN;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//如果设置了，则使用自动背光覆盖</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span> autoBrightnessAdjustment;</span><br><span class="line">        <span class="keyword">if</span> (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) &#123;</span><br><span class="line">            autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;</span><br><span class="line">            brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP;</span><br><span class="line">            mAppliedTemporaryAutoBrightnessAdjustment = <span class="keyword">true</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            autoBrightnessAdjustment = mAutoBrightnessAdjustment;</span><br><span class="line">            brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;</span><br><span class="line">            mAppliedTemporaryAutoBrightnessAdjustment = <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ....</span><br><span class="line">        <span class="comment">// Apply auto-brightness.</span></span><br><span class="line">        <span class="keyword">boolean</span> slowChange = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (Float.isNaN(brightnessState)) &#123;</span><br><span class="line">            <span class="keyword">float</span> newAutoBrightnessAdjustment = autoBrightnessAdjustment;</span><br><span class="line">            <span class="keyword">if</span> (autoBrightnessEnabled) &#123;</span><br><span class="line">                <span class="comment">//获取自动背光值</span></span><br><span class="line">                brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness();</span><br><span class="line">                <span class="comment">//获取新的adjustment值</span></span><br><span class="line">                newAutoBrightnessAdjustment =</span><br><span class="line">                        mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (isValidBrightnessValue(brightnessState)</span><br><span class="line">                    || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) &#123;</span><br><span class="line">                <span class="comment">// Use current auto-brightness value and slowly adjust to changes.</span></span><br><span class="line">                <span class="comment">//3. 使用当前的自动背光值，缓慢的变化</span></span><br><span class="line">                brightnessState = clampScreenBrightness(brightnessState);</span><br><span class="line">            .....</span><br><span class="line">                    &#125;</span><br><span class="line">            <span class="keyword">if</span> (autoBrightnessAdjustment != newAutoBrightnessAdjustment) &#123;</span><br><span class="line">                <span class="comment">//将adjustment值保存到SettingsProvider中</span></span><br><span class="line">                <span class="comment">//以确保Settings等其他进程对它的使用</span></span><br><span class="line">                putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// Adjustment values resulted in no change</span></span><br><span class="line">                brightnessAdjustmentFlags = <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">float</span> animateValue = brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT</span><br><span class="line">                    ? PowerManager.BRIGHTNESS_MIN : brightnessState;</span><br><span class="line">            <span class="keyword">if</span> (isValidBrightnessValue(animateValue)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (initialRampSkip || hasBrightnessBuckets</span><br><span class="line">                        || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) &#123;</span><br><span class="line">                    <span class="comment">//4. 设置背光到底层LED驱动，调节背光值，不设置背光动画（并且防止非法值时，保证不黑屏，设置最小背光）</span></span><br><span class="line">                    animateScreenBrightness(animateValue, SCREEN_ANIMATION_RATE_MINIMUM);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">//设置亮度值到底层驱动，调节背光值，根据slowChange设置快动画还是慢动画</span></span><br><span class="line">                    animateScreenBrightness(animateValue,</span><br><span class="line">                            slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//--------------------------------</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//1.手动设置的亮度</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">updateUserSetScreenBrightness</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//mPendingScreenBrightnessSetting是通过SettingsObserver监测Settings数据库中的值</span></span><br><span class="line">        <span class="comment">//在handleSettingsChange方法中通过getScreenBrightnessSetting()获取</span></span><br><span class="line">        <span class="keyword">if</span> ((Float.isNaN(mPendingScreenBrightnessSetting)</span><br><span class="line">                || mPendingScreenBrightnessSetting &lt; <span class="number">0.0f</span>)) &#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 class="keyword">if</span> (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) &#123;</span><br><span class="line">            mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;</span><br><span class="line">            <span class="comment">//手动设置的背光</span></span><br><span class="line">            mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;</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">        mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;</span><br><span class="line">        mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;</span><br><span class="line">        mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;</span><br><span class="line">        mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;  <span class="comment">//not a number， 0.0f / 0.0f</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="function"><span class="keyword">private</span> <span class="keyword">float</span> <span class="title">clampScreenBrightness</span><span class="params">(<span class="keyword">float</span> value)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//非法值则返回最小背光值</span></span><br><span class="line">        <span class="keyword">if</span> (Float.isNaN(value)) &#123;</span><br><span class="line">            <span class="keyword">return</span> mScreenBrightnessRangeMinimum;</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> MathUtils.constrain(</span><br><span class="line">                value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">animateScreenBrightness</span><span class="params">(<span class="keyword">float</span> target, <span class="keyword">float</span> rate)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (DEBUG) &#123;</span><br><span class="line">            Slog.d(TAG, <span class="string">"Animating brightness: target="</span> + target +<span class="string">", rate="</span> + rate);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (mScreenBrightnessRampAnimator.animateTo(target, rate)) &#123;</span><br><span class="line">            Trace.traceCounter(Trace.TRACE_TAG_POWER, <span class="string">"TargetScreenBrightness"</span>, (<span class="keyword">int</span>) target);</span><br><span class="line">            <span class="comment">// TODO(b/153319140) remove when we can get this from the above trace invocation</span></span><br><span class="line">            SystemProperties.set(<span class="string">"debug.tracing.screen_brightness"</span>, String.valueOf(target));</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// TODO(brightnessfloat): change BatteryStats to use float</span></span><br><span class="line">                mBatteryStats.noteScreenBrightness(</span><br><span class="line">                        BrightnessSynchronizer.brightnessFloatToInt(</span><br><span class="line">                        mContext, target));</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RemoteException ex) &#123;</span><br><span class="line">                <span class="comment">// same process</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><h3 id="异步任务机制AsyncTask"><a href="#异步任务机制AsyncTask" class="headerlink" title="异步任务机制AsyncTask"></a>异步任务机制AsyncTask</h3><p>在Android中实现异步任务机制有两种方式：<code>Handler和AsyncTask</code></p><p>相比于Handle模式需为每个文物创建新县城，发送消息接受消息过程精细，工具类android.os.AsyncTask使创建异步任务变得更加简单</p><p>注意点：</p><ol><li>实例必须在UI线程中创建</li><li>execute必须在UI线程中调用</li><li>不要手动调用onPreExecute，doInBackground，onProgressUpdate，onPostExecute方法</li><li>不能在doInBackground(Params… params)中更改UI组件的信</li><li>一个任务实例只能执行一次，如果执行第二次将会抛出异常</li></ol><h4 id="异步任务主要方法"><a href="#异步任务主要方法" class="headerlink" title="异步任务主要方法"></a>异步任务主要方法</h4><p><strong>AsyncTask是一个泛型类，它的三个类型参数的含义如下：</strong></p><ol><li>Params：doInBackground方法的参数类型；</li><li>Progress：AsyncTask所执行的后台任务的进度类型；</li><li>Result：后台任务的返回结果类型。</li></ol><p><strong>方法：</strong></p><ol><li>execute(Params… params)，执行一个异步任务，需要在代码中调用此方法，触发异步任务的执行</li><li>onPreExecute()，在execute(Params… params)被调用后立即执行，一般用来在执行后台任务前对UI做一些标记</li><li>doInBackground(Params… params)，在onPreExecute()完成后立即执行，用于执行较为费时的操作，此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress… values)来更新进度信息</li><li>onProgressUpdate(Progress… values)，在调用publishProgress(Progress… values)时，此方法被执行，直接将进度信息更新到UI组件上</li><li>onPostExecute(Result result)，当后台操作结束时，此方法将会被调用，计算结果将做为参数传递到此方法中，直接将结果显示到UI组件</li></ol><hr><h2 id="自动背光调节"><a href="#自动背光调节" class="headerlink" title="自动背光调节"></a>自动背光调节</h2><h3 id="主要变量"><a href="#主要变量" class="headerlink" title="主要变量"></a>主要变量</h3><p>代码主要在<code>frameworks/base/services/core/java/com/android/server/display</code>目录下的<code>DisplayPowerController.java</code>和<code>AutomaticBrightnessController.java</code></p><ol><li>在<code>AutomaticBrightnessController.java</code>中定义了一些自动背光的变量：</li></ol><ul><li>mScreenAutoBrightness：屏幕亮度级别是由自动亮度算法决定的，实际的亮度应向这个值靠拢。我们保留这个值，即使我们停止使用光传感器，以便我们可以快速恢复到之前的自动亮度级别</li><li>mResetAmbientLuxAfterWarmUpConfig：如果设置为true，屏幕点亮后，控制器根据当前传感器读到的值调整亮度；如果是false，控制器将收集更多的数据，然后决定是否改变亮度</li><li>mAmbientLux：当前接收的环境光级别</li><li>mAmbientLightRingBuffer：用来保存最近环境光传感器读值的环形传感器</li><li>AMBIENT_LIGHT_PREDICTION_TIME_MILLIS=100：假定当前传感器读数超出当前时间的有效期，并且确保最后样本的权重非0，这反过来确保了总权重非0</li><li>mLightSensorWarmUpTimeConfig：亮屏后在等待光传感器准备时自动亮度调整时间，以毫秒为单位。该值在创建AutomaticBrightnessController对象时被入参赋值</li></ul><ol start="2"><li><p><code>BrightnessMappingStrategy</code>类负责创建背光曲线（从BrightnessConfigure类中读取两个数组源:config_autoBrightnessLevels和config_autoBrightnessDisplayValuesNits），计算自动背光值</p></li><li><p>背光相关名词</p></li></ol><ul><li>光照度（照度）：从光源照射到单位面积上的光通量,以E表示,照度的单位为勒克斯(Lux)</li></ul><hr><h3 id="DisplayPowerController初始化背光属性变量"><a href="#DisplayPowerController初始化背光属性变量" class="headerlink" title="DisplayPowerController初始化背光属性变量"></a>DisplayPowerController初始化背光属性变量</h3><ol><li>首先通过initPowerManagement方法new一个DisplayPowerController对象</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></pre></td><td class="code"><pre><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">LocalService</span> <span class="keyword">extends</span> <span class="title">DisplayManagerInternal</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">initPowerManagement</span><span class="params">(<span class="keyword">final</span> DisplayPowerCallbacks callbacks, Handler handler,</span></span></span><br><span class="line"><span class="function"><span class="params">                SensorManager sensorManager)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (mSyncRoot) &#123;</span><br><span class="line">                DisplayBlanker blanker = <span class="keyword">new</span> DisplayBlanker() &#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">requestDisplayState</span><span class="params">(<span class="keyword">int</span> state, <span class="keyword">float</span> brightness)</span> </span>&#123;</span><br><span class="line">                        .......</span><br><span class="line">                &#125;;</span><br><span class="line">                <span class="comment">//创建DisplayPowerController对象</span></span><br><span class="line">                mDisplayPowerController = <span class="keyword">new</span> DisplayPowerController(</span><br><span class="line">                        mContext, callbacks, handler, sensorManager, blanker,</span><br><span class="line">                        mDisplayDevices.get(Display.DEFAULT_DISPLAY));</span><br><span class="line">                mSensorManager = sensorManager;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>DisplayPowerController构造函数中初始化了很多和亮度相关的属性变量。</li></ol><ul><li>mBatteryStats：获取电池信息service状态用于更新电池电量</li><li>mSensorManager：获取SensorManager用于调节吧诶广</li><li>mWindowManagerPolicy： 亮屏时调用到window绘制屏幕</li><li>mBlanker：亮屏以及设置背光时调用到DisplayPowerState的中介类对象</li><li>mScreenBrightnessDozeConfig：Doze状态配置亮度</li><li>mScreenBrightnessDimConfig：暗屏状态配置亮度</li><li>mScreenBrightnessRangeMaximum：屏幕最大亮度</li><li>mScreenBrightnessRangeMinimum：屏幕最小亮度</li><li>mUseSoftwareAutoBrightnessConfig：是否支持自动亮度</li><li>lightSensorWarmUpTimeConfig：Light sensor启动时间</li><li>lightSensorRate：sensor采集数据频率，配置在frameworks/base/core/res/res/values/config.xml，默认250</li><li>brighteningLightDebounce：变亮防抖时间，同上，默认4000</li><li>darkeningLightDebounce：变暗防抖时间，同上，默认8000</li><li>autoBrightnessResetAmbientLuxAfterWarmUp：当sensor启动时重置环境光照值，同上，默认true</li></ul><hr><h3 id="创建背光曲线（映射）"><a href="#创建背光曲线（映射）" class="headerlink" title="创建背光曲线（映射）"></a>创建背光曲线（映射）</h3><h4 id="创建曲线示意图"><a href="#创建曲线示意图" class="headerlink" title="创建曲线示意图"></a>创建曲线示意图</h4><p>整体来看，共创建了三条样条曲线，对Lux-Nit-Backlight进行映射。和8.1不同的是，并非直接由Lux和Baclight映射，而是将Nit作为Lux和Backlight的中间介质。</p><ol><li>mNitsToBacklightSpline</li><li>mBacklightToNitsSpline</li><li>mBrightnessSpline</li></ol><p><img src="CreateMappingSpline.png" alt="曲线"></p><p>流程：</p><p><img src="CreateMappingSplineCode.png" alt="流程"></p><hr><h3 id="构造函数开始创建"><a href="#构造函数开始创建" class="headerlink" title="构造函数开始创建"></a>构造函数开始创建</h3><p>在上面DisplayPowerController构造函数中会调用以下函数：<code>mBrightnessMapper = BrightnessMappingStrategy.create(resources);</code></p><p>而BrightnessMappingStrategy.java的create函数中获取配置文件config.xml中的配置值（厂商会覆盖重新配置，一般路径会有overlay），然后根据这个配置值决定映射方式：</p><ul><li>config_autoBrightnessLevels：环境光lux值，等级和背光对应</li><li>config_autoBrightnessLcdBacklightValues：背光等级</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Lux值数组--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">integer-array</span> <span class="attr">name</span>=<span class="string">"config_autoBrightnessLevels"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">integer-array</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- Lux值对应的背光值数组 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">integer-array</span> <span class="attr">name</span>=<span class="string">"config_autoBrightnessLcdBacklightValues"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">integer-array</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- ##### 以下三组是Android Q之后添加 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- Lux值对应的Nits值数组 --&gt;</span>  </span><br><span class="line"><span class="tag">&lt;<span class="name">array</span> <span class="attr">name</span>=<span class="string">"config_autoBrightnessDisplayValuesNits"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">array</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 描述屏幕发光强度的Nits值数组 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">array</span> <span class="attr">name</span>=<span class="string">"config_screenBrightnessNits"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">array</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 和发光强度Nits值对应的背光值数组 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">integer-array</span> <span class="attr">name</span>=<span class="string">"config_screenBrightnessBacklight"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">integer-array</span>&gt;</span></span><br></pre></td></tr></table></figure><p>创建函数：</p><figure class="highlight java"><figcaption><span>frameworks/base/services/core/java/com/android/server/display/BrightnessMappingStrategy.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="meta">@Nullable</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> BrightnessMappingStrategy <span class="title">create</span><span class="params">(Resources resources)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//lux环境光数组</span></span><br><span class="line">    <span class="keyword">float</span>[] luxLevels = getLuxLevels(resources.getIntArray(</span><br><span class="line">            com.android.internal.R.array.config_autoBrightnessLevels));</span><br><span class="line">    <span class="comment">//Lux值对应的背光值</span></span><br><span class="line">    <span class="keyword">int</span>[] brightnessLevelsBacklight = resources.getIntArray(</span><br><span class="line">            com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);</span><br><span class="line">    <span class="comment">//和Lux值对应的屏幕亮度的Nits值数组，长度比Lux值数组大1。如果配置了该值，则：</span></span><br><span class="line">    <span class="comment">//1.config_screenBrightnessNits必须配置</span></span><br><span class="line">    <span class="comment">//2.config_screenBrightnessBacklight必须配置</span></span><br><span class="line">    <span class="keyword">float</span>[] brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(</span><br><span class="line">            com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));</span><br><span class="line">    <span class="comment">//用户可调整的gamma最大值</span></span><br><span class="line">    <span class="keyword">float</span> autoBrightnessAdjustmentMaxGamma = resources.getFraction(</span><br><span class="line">            com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,</span><br><span class="line">            <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">//描述屏幕亮度的nits值数组</span></span><br><span class="line">    <span class="keyword">float</span>[] nitsRange = getFloatArray(resources.obtainTypedArray(</span><br><span class="line">            com.android.internal.R.array.config_screenBrightnessNits));</span><br><span class="line">    <span class="comment">//与nitsRange数组中的亮度值(单位为Nits)相对应的屏幕背光值</span></span><br><span class="line">    <span class="keyword">int</span>[] backlightRange = resources.getIntArray(</span><br><span class="line">            com.android.internal.R.array.config_screenBrightnessBacklight);</span><br><span class="line">    <span class="keyword">long</span> shortTermModelTimeout = resources.getInteger(</span><br><span class="line">            com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);</span><br><span class="line">    <span class="comment">//判断是否是有效映射:</span></span><br><span class="line">    <span class="comment">//1.非空</span></span><br><span class="line">    <span class="comment">//2.长度相同</span></span><br><span class="line">    <span class="comment">//3.元素&gt;=0;4.nitsRange/luxLevels必须递增,backlightRange/brightnessLevelsNits必须非递减</span></span><br><span class="line">    <span class="keyword">if</span> (isValidMapping(nitsRange, backlightRange)</span><br><span class="line">            &amp;&amp; isValidMapping(luxLevels, brightnessLevelsNits)) &#123;</span><br><span class="line">        <span class="comment">//最小背光值1</span></span><br><span class="line">        <span class="keyword">int</span> minimumBacklight = resources.getInteger(</span><br><span class="line">                com.android.internal.R.integer.config_screenBrightnessSettingMinimum);</span><br><span class="line">        <span class="comment">//最大背光值255</span></span><br><span class="line">        <span class="keyword">int</span> maximumBacklight = resources.getInteger(</span><br><span class="line">                com.android.internal.R.integer.config_screenBrightnessSettingMaximum);</span><br><span class="line">        <span class="comment">//创建BrightnessConfiguration.Builder实例对象</span></span><br><span class="line">        <span class="comment">//将luxLevels和nits值保存进去</span></span><br><span class="line">        BrightnessConfiguration.Builder builder = <span class="keyword">new</span> BrightnessConfiguration.Builder(</span><br><span class="line">                luxLevels, brightnessLevelsNits);</span><br><span class="line">        builder.setShortTermModelTimeoutMillis(shortTermModelTimeout);</span><br><span class="line">        builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);</span><br><span class="line">        builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);</span><br><span class="line">        <span class="comment">//（1） 映射Lux值和Nits值，而非Lux值和直接显示的背光值，物理映射</span></span><br><span class="line">        <span class="comment">//一般执行该流程创建曲线</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,</span><br><span class="line">                autoBrightnessAdjustmentMaxGamma);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isValidMapping(luxLevels, brightnessLevelsBacklight)) &#123;</span><br><span class="line">        <span class="comment">//（2） 直接映射Lux值和背光值，简单映射</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,</span><br><span class="line">                autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="（1）-PhysicalMappingStrategy构造函数"><a href="#（1）-PhysicalMappingStrategy构造函数" class="headerlink" title="（1） PhysicalMappingStrategy构造函数"></a>（1） PhysicalMappingStrategy构造函数</h3><p>一般执行此处流程：</p><figure class="highlight java"><figcaption><span>BrightnessMappingStrategy.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">//（1） PhysicalMappingStrategy构造函数，创建两条映射曲线</span></span><br><span class="line">    <span class="meta">@VisibleForTesting</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">PhysicalMappingStrategy</span> <span class="keyword">extends</span> <span class="title">BrightnessMappingStrategy</span> </span>&#123;</span><br><span class="line">        ....</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">PhysicalMappingStrategy</span><span class="params">(BrightnessConfiguration config, <span class="keyword">float</span>[] nits,</span></span></span><br><span class="line"><span class="function"><span class="params">                                       <span class="keyword">int</span>[] backlight, <span class="keyword">float</span> maxGamma)</span> </span>&#123;</span><br><span class="line">            .....</span><br><span class="line">            mMaxGamma = maxGamma;</span><br><span class="line">            <span class="comment">//自动亮度调节</span></span><br><span class="line">            mAutoBrightnessAdjustment = <span class="number">0</span>;</span><br><span class="line">            <span class="comment">//自动亮度开启的情况下，用户手动调节亮度时的当前lux值</span></span><br><span class="line">            mUserLux = -<span class="number">1</span>;</span><br><span class="line">            <span class="comment">//自动亮度开启的情况下，用户手动调节亮度设置的亮度值</span></span><br><span class="line">            mUserBrightness = -<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Setup the backlight spline</span></span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">int</span> N = nits.length;</span><br><span class="line">            <span class="keyword">float</span>[] normalizedBacklight = <span class="keyword">new</span> <span class="keyword">float</span>[N];</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">                normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//创建Nits-Backlight样条曲线</span></span><br><span class="line">            mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);</span><br><span class="line">            <span class="comment">//创建Backlight-Nits样条曲线</span></span><br><span class="line">            mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);</span><br><span class="line"></span><br><span class="line">            mDefaultConfig = config;</span><br><span class="line">            <span class="keyword">if</span> (mLoggingEnabled) &#123;</span><br><span class="line">                PLOG.start(<span class="string">"physical mapping strategy"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            mConfig = config;</span><br><span class="line">            <span class="comment">//根据不同的场景创建Lux-nits样条曲线</span></span><br><span class="line">            computeSpline();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="（1-1）-PhysicalMappingStrategy中的computeSpline"><a href="#（1-1）-PhysicalMappingStrategy中的computeSpline" class="headerlink" title="（1.1） PhysicalMappingStrategy中的computeSpline"></a>（1.1） PhysicalMappingStrategy中的computeSpline</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></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">computeSpline</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//获取BrightnessConfiguration中的Lux数组和Lux值对应的Nits数组，并放入Pair对象中</span></span><br><span class="line">        Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; defaultCurve = mConfig.getCurve();</span><br><span class="line">        <span class="keyword">float</span>[] defaultLux = defaultCurve.first;    <span class="comment">//lux数组</span></span><br><span class="line">        <span class="keyword">float</span>[] defaultNits = defaultCurve.second;  <span class="comment">//和lux对应的nits数组</span></span><br><span class="line">        <span class="comment">//创建一个和defaultNits等长的数组，用于存放对应的背光值（从曲线中获取）</span></span><br><span class="line">        <span class="keyword">float</span>[] defaultBacklight = <span class="keyword">new</span> <span class="keyword">float</span>[defaultNits.length];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; defaultBacklight.length; i++) &#123;</span><br><span class="line">            <span class="comment">//从mNitsToBacklightSpline中获取背光值，即根据config_autoBrightnessDisplayValuesNits值</span></span><br><span class="line">            <span class="comment">//从config_screenBrightnessNits与config_screenBrightnessBacklight的曲线中获取默认的背光值</span></span><br><span class="line">            defaultBacklight[i] = mNitsToBacklightSpline.interpolate(defaultNits[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//对上面获取的背光值进一步加工，如果用户设置过亮度，需将该亮度也加入曲线，</span></span><br><span class="line">        <span class="comment">//最终得到调整后的lux数组和brightness数组</span></span><br><span class="line">        Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; curve = getAdjustedCurve(defaultLux, defaultBacklight, mUserLux,</span><br><span class="line">                mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);</span><br><span class="line">        <span class="comment">//最终的lux数组和backlight数组</span></span><br><span class="line">        <span class="keyword">float</span>[] lux = curve.first;</span><br><span class="line">        <span class="keyword">float</span>[] backlight = curve.second;</span><br><span class="line">        <span class="keyword">float</span>[] nits = <span class="keyword">new</span> <span class="keyword">float</span>[backlight.length];</span><br><span class="line">        <span class="comment">//根据背光值，从config_screenBrightnessNits和onfig_screenBrightnessBacklight构建的mBacklightToNitsSpline曲线中获取Nit值</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; nits.length; i++) &#123;</span><br><span class="line">            nits[i] = mBacklightToNitsSpline.interpolate(backlight[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//完成创建Lux-nits样条曲线</span></span><br><span class="line">        <span class="comment">//最终的背光值从此曲线和mNitsToBacklightSpline曲线中获取</span></span><br><span class="line">        mBrightnessSpline = Spline.createSpline(lux, nits);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; getCurve() &#123;</span><br><span class="line">    <span class="keyword">return</span> Pair.create(Arrays.copyOf(mLux, mLux.length), Arrays.copyOf(mNits, mNits.length));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Pair容器类"><a href="#Pair容器类" class="headerlink" title="Pair容器类"></a>Pair容器类</h4><p>Pair对两个对象组成的元素组进行传递。这个对象提供了一个合理的equals()方法，如果两个对象的first和second值相等则返回true</p><p>特征：</p><ol><li>Pair(F first, S second)，一个Pair容器里面有2个元素，他们成组存在。</li><li>Pair里面两个元素都是final的</li><li>Pair的equals是值比较，不是地址比较</li></ol><p>示例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">String PAIR = <span class="string">"PAIR"</span>;</span><br><span class="line">Pair p1 = <span class="keyword">new</span> Pair(<span class="number">18</span>, <span class="string">"张三"</span>); <span class="comment">// 通过 构造函数 实例化对象</span></span><br><span class="line">Pair p2 = Pair.create(<span class="number">20</span>, <span class="string">"李四"</span>);<span class="comment">//通过 create方法 实例化对象</span></span><br><span class="line"><span class="keyword">boolean</span> e1 = p1.equals(p2);</span><br><span class="line">Log.d(PAIR, <span class="string">"RESULT: "</span> + e1); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><hr><h3 id="（2）-SimpleMappingStrategy构造函数"><a href="#（2）-SimpleMappingStrategy构造函数" class="headerlink" title="（2） SimpleMappingStrategy构造函数"></a>（2） SimpleMappingStrategy构造函数</h3><figure class="highlight java"><figcaption><span>BrightnessMappingStrategy.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">//（2） 调用SimpleMappingStrategy构造函数</span></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">SimpleMappingStrategy</span><span class="params">(<span class="keyword">float</span>[] lux, <span class="keyword">int</span>[] brightness, <span class="keyword">float</span> maxGamma,</span></span></span><br><span class="line"><span class="function"><span class="params">                <span class="keyword">long</span> timeout)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">int</span> N = brightness.length;</span><br><span class="line">            mLux = <span class="keyword">new</span> <span class="keyword">float</span>[N];</span><br><span class="line">            mBrightness = <span class="keyword">new</span> <span class="keyword">float</span>[N];</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">                mLux[i] = lux[i];</span><br><span class="line">                mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            mMaxGamma = maxGamma;</span><br><span class="line">            mAutoBrightnessAdjustment = <span class="number">0</span>;</span><br><span class="line">            mUserLux = -<span class="number">1</span>;</span><br><span class="line">            mUserBrightness = -<span class="number">1</span>;</span><br><span class="line">            <span class="comment">//映射lux和brightness曲线</span></span><br><span class="line">            computeSpline();</span><br><span class="line">            mShortTermModelTimeout = timeout;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><h3 id="（2-1）-SimpleMappingStrategy中的computeSpline"><a href="#（2-1）-SimpleMappingStrategy中的computeSpline" class="headerlink" title="（2.1） SimpleMappingStrategy中的computeSpline"></a>（2.1） SimpleMappingStrategy中的computeSpline</h3><figure class="highlight java"><figcaption><span>BrightnessMappingStrategy.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">computeSpline</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//计算调整，得到调整后的Lux值数组和backlight值数组</span></span><br><span class="line">    Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; curve = getAdjustedCurve(mLux, mBrightness, mUserLux,</span><br><span class="line">            mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);</span><br><span class="line">    <span class="comment">//Lux-Nits曲线,最终的背光值从此曲线+mNitsToBacklightSpline曲线获取</span></span><br><span class="line">    mSpline = Spline.createSpline(curve.first, curve.second);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="AutomaticBrightnessController初始化"><a href="#AutomaticBrightnessController初始化" class="headerlink" title="AutomaticBrightnessController初始化"></a>AutomaticBrightnessController初始化</h2><p>在DisplayPowerController构造函数中创建了AutomaticBrightnessController对象</p><figure class="highlight java"><figcaption><span>frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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><br><span class="line">mBrightnessMapper = BrightnessMappingStrategy.create(resources);</span><br><span class="line"><span class="comment">//背光曲线不为空</span></span><br><span class="line"><span class="keyword">if</span> (mBrightnessMapper != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="comment">//实例化</span></span><br><span class="line">    mAutomaticBrightnessController = <span class="keyword">new</span> AutomaticBrightnessController(<span class="keyword">this</span>,</span><br><span class="line">            handler.getLooper(), sensorManager, lightSensor, mBrightnessMapper,</span><br><span class="line">            lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,</span><br><span class="line">            mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,</span><br><span class="line">            initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,</span><br><span class="line">            autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,</span><br><span class="line">            screenBrightnessThresholds, context, displayDeviceConfig);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    mUseSoftwareAutoBrightnessConfig = <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line">....</span><br></pre></td></tr></table></figure><h3 id="configure方法配置AutomaticBrightnessController"><a href="#configure方法配置AutomaticBrightnessController" class="headerlink" title="*configure方法配置AutomaticBrightnessController"></a>*configure方法配置AutomaticBrightnessController</h3><ol><li>在<code>updatePowerState()</code>中也会对mAutomaticBrightnessController对象进行配置：</li></ol><figure class="highlight java"><figcaption><span>DisplayPowerController.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">updatePowerState</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        .......</span><br><span class="line">        <span class="comment">// If the brightness is already set then it's been overridden by something other than the</span></span><br><span class="line">        <span class="comment">// user, or is a temporary adjustment.</span></span><br><span class="line">        <span class="keyword">boolean</span> userInitiatedChange = (Float.isNaN(brightnessState))</span><br><span class="line">                &amp;&amp; (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);</span><br><span class="line">        <span class="keyword">boolean</span> hadUserBrightnessPoint = <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// Configure auto-brightness.</span></span><br><span class="line">        <span class="keyword">if</span> (mAutomaticBrightnessController != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">//曲线中是否有用户设置的短期点</span></span><br><span class="line">            hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();</span><br><span class="line">            mAutomaticBrightnessController.configure(autoBrightnessEnabled,</span><br><span class="line">                    mBrightnessConfiguration,</span><br><span class="line">                    mLastUserSetScreenBrightness,</span><br><span class="line">                    userSetBrightnessChanged, autoBrightnessAdjustment,</span><br><span class="line">                    autoBrightnessAdjustmentChanged, mPowerRequest.policy);</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="comment">//表示是否在自动背光打开的情况下拖动亮度条调节过亮度</span></span><br><span class="line"><span class="comment">//判断依据是BrightnessMappingStrategy中的mUserLux成员，它表示用户在开启自动背光后手动设置亮度时的Lux值</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">hasUserDataPoints</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> mUserLux != -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>配置的参数：</p><ul><li>autoBrightnessEnabled：表示自动背光是否可用</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">boolean</span> autoBrightnessEnabledInDoze =</span><br><span class="line">        mAllowAutoBrightnessWhileDozingConfig &amp;&amp; Display.isDozeState(state);</span><br><span class="line"><span class="comment">//打开了自动亮度调节&amp;&amp;(亮屏或Doze)&amp;&amp;局部变量brightness为0&amp;&amp;BrightnessMappingStrategy不为空</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">boolean</span> autoBrightnessEnabled = mPowerRequest.useAutoBrightness</span><br><span class="line">            &amp;&amp; (state == Display.STATE_ON || autoBrightnessEnabledInDoze)</span><br><span class="line">            &amp;&amp; Float.isNaN(brightnessState)</span><br><span class="line">            &amp;&amp; mAutomaticBrightnessController != <span class="keyword">null</span>;</span><br></pre></td></tr></table></figure><ul><li>mBrightnessConfiguration：BrightnessConfiguration对象，携带有用于创建曲线的Lux值数组和对应的Nit值数组。每一个用户可对应一个BrightnessConfiguration，由DisplayManagerService设置</li><li>userSetBrightnessChanged：用户是否手动通过拖动亮度条设置过亮度</li><li>autoBrightnessAdjustmentChanged：自动背光调整至adjustment是否发生变化（对应的即下面configure中的<code>userChangedAutoBrightnessAdjustment</code>）</li><li>displayPolicy：当前请求的屏幕状态</li></ul><hr><ol start="2"><li>configure()函数方法</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><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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(<span class="keyword">boolean</span> enable, @Nullable BrightnessConfiguration configuration,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">float</span> brightness, <span class="keyword">boolean</span> userChangedBrightness, <span class="keyword">float</span> adjustment,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">boolean</span> userChangedAutoBrightnessAdjustment, <span class="keyword">int</span> displayPolicy)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//是否要进入Doze状态</span></span><br><span class="line">    <span class="keyword">boolean</span> dozing = (displayPolicy == DisplayPowerRequest.POLICY_DOZE);</span><br><span class="line">    <span class="comment">//step 1: 设置BrightnessConfiguration对象，若发生变化返回true</span></span><br><span class="line">    <span class="keyword">boolean</span> changed = setBrightnessConfiguration(configuration);</span><br><span class="line">    <span class="comment">//step 2: 设置Display状态，若发生变化返回true</span></span><br><span class="line">    changed |= setDisplayPolicy(displayPolicy);</span><br><span class="line">    <span class="comment">//step 3: 如果用户改变自动背光调节值，设置自动背光调节值</span></span><br><span class="line">    <span class="keyword">if</span> (userChangedAutoBrightnessAdjustment) &#123;</span><br><span class="line">        changed |= setAutoBrightnessAdjustment(adjustment);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//step 4: 如果在自动亮度开启的情况下调节了亮度，需要将当前的Lux值和用户设置的亮度添加到曲线中</span></span><br><span class="line">    <span class="keyword">if</span> (userChangedBrightness &amp;&amp; enable) &#123;</span><br><span class="line">        <span class="comment">// Update the brightness curve with the new user control point. It's critical this</span></span><br><span class="line">        <span class="comment">// happens after we update the autobrightness adjustment since it may reset it.</span></span><br><span class="line">        changed |= setScreenBrightnessByUser(brightness);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">boolean</span> userInitiatedChange =</span><br><span class="line">            userChangedBrightness || userChangedAutoBrightnessAdjustment;</span><br><span class="line">    <span class="keyword">if</span> (userInitiatedChange &amp;&amp; enable &amp;&amp; !dozing) &#123;</span><br><span class="line">        <span class="comment">//step 5</span></span><br><span class="line">        prepareBrightnessAdjustmentSample();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//step 6: 注册LightSensor</span></span><br><span class="line">    changed |= setLightSensorEnabled(enable &amp;&amp; !dozing);</span><br><span class="line">    <span class="comment">//step 7: 如果changed为true，更新自动背光亮度值，但不会主动调用DPC更新背光</span></span><br><span class="line">    <span class="keyword">if</span> (changed) &#123;</span><br><span class="line">        updateAutoBrightness(<span class="keyword">false</span> <span class="comment">/*sendUpdate*/</span>, userInitiatedChange);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="1-setBrightnessConfiguration"><a href="#1-setBrightnessConfiguration" class="headerlink" title="1. setBrightnessConfiguration"></a>1. setBrightnessConfiguration</h4><ol><li>针对每个用户，都会有一个BrightnessConfiguration和它映射，所以当切换用户后，设备的背光将可能发生改变</li><li>当BrightnessConfiguration发生改变且在BrighnessMappingStragety中设置完成后，将会通过resetShortTermModel()清除原有用户的配置</li></ol><p>清除用户设置的亮度和对应Lux的三种情况：</p><ol><li>切换用户：handleSettingsChange</li><li>BrightnessConfigure对象发生变化：setBrightnessConfiguration</li><li>屏幕进入不可交互状态超过30s，且再次进入交互状态后环境光发生变化：setDisplayPolicy</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><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">//display/AutomaticBrightnessController.java</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">setBrightnessConfiguration</span><span class="params">(BrightnessConfiguration configuration)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//Step 1</span></span><br><span class="line">        <span class="keyword">if</span> (mBrightnessMapper.setBrightnessConfiguration(configuration)) &#123;</span><br><span class="line">            <span class="comment">//Step 2</span></span><br><span class="line">            resetShortTermModel();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">resetShortTermModel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//清楚用户添加的曲线Points</span></span><br><span class="line">        mBrightnessMapper.clearUserDataPoints();</span><br><span class="line">        <span class="comment">//表示短期模型仍有效</span></span><br><span class="line">        mShortTermModelValid = <span class="keyword">true</span>;</span><br><span class="line">        mShortTermModelAnchor = -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//display/BrightnessMappingStrategy.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">boolean</span> <span class="title">setBrightnessConfiguration</span><span class="params">(@Nullable BrightnessConfiguration config)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (config == <span class="keyword">null</span>) &#123;</span><br><span class="line">                config = mDefaultConfig;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (config.equals(mConfig)) &#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 class="keyword">if</span> (mLoggingEnabled) &#123;</span><br><span class="line">                PLOG.start(<span class="string">"brightness configuration"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//如果config对象发生改变，则替换后，会调用computeSpline重新创建Lux-Nits曲线</span></span><br><span class="line">            mConfig = config;</span><br><span class="line">            computeSpline();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clearUserDataPoints</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (mUserLux != -<span class="number">1</span>) &#123;</span><br><span class="line">                <span class="comment">//重置变量</span></span><br><span class="line">                mAutoBrightnessAdjustment = <span class="number">0</span>;</span><br><span class="line">                mUserLux = -<span class="number">1</span>;</span><br><span class="line">                mUserBrightness = -<span class="number">1</span>;</span><br><span class="line">                <span class="comment">//重新创建曲线</span></span><br><span class="line">                computeSpline();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><h4 id="2-setDisplayPolicy"><a href="#2-setDisplayPolicy" class="headerlink" title="2. setDisplayPolicy"></a>2. setDisplayPolicy</h4><p>该方法主要根据屏幕状态来设置mShortTermModelValid的值</p><p>当屏幕状态进入Doze或者Asleep后，会发送一个定义Handler，并在到达时间后将mShortTermModelValid值置为false</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//display/AutomaticBrightnessController.java</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">setDisplayPolicy</span><span class="params">(<span class="keyword">int</span> policy)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mDisplayPolicy == policy) &#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 class="keyword">final</span> <span class="keyword">int</span> oldPolicy = mDisplayPolicy;</span><br><span class="line">        mDisplayPolicy = policy;</span><br><span class="line">        <span class="comment">//如果Display状态值由交互变为不可交互状态</span></span><br><span class="line">        <span class="keyword">if</span> (!isInteractivePolicy(policy) &amp;&amp; isInteractivePolicy(oldPolicy)) &#123;</span><br><span class="line">            <span class="comment">//发送一个30s的延迟消息，30s后将mShortTermModelValid置为false</span></span><br><span class="line">            mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,</span><br><span class="line">                    mBrightnessMapper.getShortTermModelTimeout());</span><br><span class="line">        <span class="comment">//如果Display状态值由不可交互变为交互状态，移除延时消息的发送</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isInteractivePolicy(policy) &amp;&amp; !isInteractivePolicy(oldPolicy)) &#123;</span><br><span class="line">            mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);</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><h4 id="3-setAutoBrightnessAdjustment"><a href="#3-setAutoBrightnessAdjustment" class="headerlink" title="3. setAutoBrightnessAdjustment"></a>3. setAutoBrightnessAdjustment</h4><p>用于设置自动背光调整值，前提是自动背光调整值已经发生变化</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//display/AutomaticBrightnessController.java</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">setAutoBrightnessAdjustment</span><span class="params">(<span class="keyword">float</span> adjustment)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mBrightnessMapper.setAutoBrightnessAdjustment(adjustment);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//display/BrightnessMappingStrategy.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">boolean</span> <span class="title">setAutoBrightnessAdjustment</span><span class="params">(<span class="keyword">float</span> adjustment)</span> </span>&#123;</span><br><span class="line">            <span class="comment">//限制，取值范围为[-1,1]</span></span><br><span class="line">            adjustment = MathUtils.constrain(adjustment, -<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">            <span class="comment">//adjustment未发生变化</span></span><br><span class="line">            <span class="keyword">if</span> (adjustment == mAutoBrightnessAdjustment) &#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 class="keyword">if</span> (mLoggingEnabled) &#123;</span><br><span class="line">                Slog.d(TAG, <span class="string">"setAutoBrightnessAdjustment: "</span> + mAutoBrightnessAdjustment + <span class="string">" =&gt; "</span> +</span><br><span class="line">                        adjustment);</span><br><span class="line">                PLOG.start(<span class="string">"auto-brightness adjustment"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//adjustment发生变化，则重新创建曲线</span></span><br><span class="line">            mAutoBrightnessAdjustment = adjustment;</span><br><span class="line">            computeSpline();</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><h4 id="4-setScreenBrightnessByUser"><a href="#4-setScreenBrightnessByUser" class="headerlink" title="4. setScreenBrightnessByUser"></a>4. setScreenBrightnessByUser</h4><p>用于将用户拖动亮度条设置的亮度和当时的Lux值添加到用于创建曲线的数组中，并重新创建曲线</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">//AutomaticBrightnessController.java</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">boolean</span> <span class="title">setScreenBrightnessByUser</span><span class="params">(<span class="keyword">float</span> brightness)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!mAmbientLuxValid) &#123;</span><br><span class="line">            <span class="comment">// If we don't have a valid ambient lux then we don't have a valid brightness anyway,</span></span><br><span class="line">            <span class="comment">// and we can't use this data to add a new control point to the short-term model.</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="comment">//将当前的Lux值和用户设置的背光值添加到曲线中</span></span><br><span class="line">        mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);</span><br><span class="line">        <span class="comment">//更新设置当前lux值</span></span><br><span class="line">        mShortTermModelValid = <span class="keyword">true</span>;</span><br><span class="line">        mShortTermModelAnchor = mAmbientLux;</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><h4 id="5-prepareBrightnessAdjustmentSample"><a href="#5-prepareBrightnessAdjustmentSample" class="headerlink" title="5. prepareBrightnessAdjustmentSample"></a>5. prepareBrightnessAdjustmentSample</h4><p>调用configure更新时，做一些标记记录</p><h4 id="6-setLightSensorEnabled"><a href="#6-setLightSensorEnabled" class="headerlink" title="6. setLightSensorEnabled"></a>6. setLightSensorEnabled</h4><p>用于LightSensor的注册和解除注册，当设备处于亮屏状态且打开自动背光功能时，将注册一个LightSensor，以监听环境光的强度变化</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//AutomaticBrightnessController.java</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">setLightSensorEnabled</span><span class="params">(<span class="keyword">boolean</span> enable)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (enable) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!mLightSensorEnabled) &#123;</span><br><span class="line">                ....</span><br><span class="line">                registerForegroundAppUpdater();</span><br><span class="line">                <span class="comment">//注册</span></span><br><span class="line">                mSensorManager.registerListener(mLightSensorListener, mLightSensor,</span><br><span class="line">                        mCurrentLightSensorRate * <span class="number">1000</span>, mHandler);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mLightSensorEnabled) &#123;</span><br><span class="line">            .....</span><br><span class="line">            mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);</span><br><span class="line">            unregisterForegroundAppUpdater();</span><br><span class="line">            <span class="comment">//解除</span></span><br><span class="line">            mSensorManager.unregisterListener(mLightSensorListener);</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><h4 id="7-updateAutoBrightness自动背光获取"><a href="#7-updateAutoBrightness自动背光获取" class="headerlink" title="7. *updateAutoBrightness自动背光获取"></a>7. *updateAutoBrightness自动背光获取</h4><p>更新自动背光亮度值</p><p>从这处流程可以看出自动背光获取的方式：</p><ol><li>首先会根据当前的Lux值，从mBrightnessSpline曲线中寻找对应的Nit值</li><li>然后根据Nit值，从曲线mNitsToBacklightSpline中获取到最终的背光值</li></ol><p>其中mBrightnessSpline曲线是由Lux值数组和它对应的Nit值数组创建，mNitsToBacklightSpline是由配置文件中的config_screenBrightnessNits和config_screenBrightnessBacklight创建</p><p>具体的创建在computeSline()方法中</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//AutomaticBrightnessController.java</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">updateAutoBrightness</span><span class="params">(<span class="keyword">boolean</span> sendUpdate, <span class="keyword">boolean</span> isManuallySet)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!mAmbientLuxValid) &#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">//根据lux值从曲线中获取背光值（区间是[0, 1.0]的浮点数）</span></span><br><span class="line">        <span class="keyword">float</span> value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,</span><br><span class="line">                mForegroundAppCategory);</span><br><span class="line">        <span class="comment">//得到最终的自动背光值</span></span><br><span class="line">        <span class="keyword">float</span> newScreenAutoBrightness = clampScreenBrightness(value);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!BrightnessSynchronizer.floatEquals(mScreenAutoBrightness,</span><br><span class="line">                newScreenAutoBrightness)) &#123;</span><br><span class="line">            mScreenAutoBrightness = newScreenAutoBrightness;</span><br><span class="line">            mScreenBrighteningThreshold = clampScreenBrightness(</span><br><span class="line">                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));</span><br><span class="line">            mScreenDarkeningThreshold = clampScreenBrightness(</span><br><span class="line">                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));</span><br><span class="line">            <span class="comment">//背光更新</span></span><br><span class="line">            <span class="keyword">if</span> (sendUpdate) &#123;</span><br><span class="line">                mCallbacks.updateBrightness();</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">//BrightnessMappingStrategy.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">float</span> <span class="title">getBrightness</span><span class="params">(<span class="keyword">float</span> lux, String packageName,</span></span></span><br><span class="line"><span class="function"><span class="params">                @ApplicationInfo.Category <span class="keyword">int</span> category)</span> </span>&#123;</span><br><span class="line">            <span class="comment">//根据lux从曲线中获取nits，然后获取背光</span></span><br><span class="line">            <span class="keyword">float</span> nits = mBrightnessSpline.interpolate(lux);</span><br><span class="line">            <span class="keyword">float</span> backlight = mNitsToBacklightSpline.interpolate(nits);</span><br><span class="line">            ...</span><br><span class="line">            <span class="keyword">return</span> backlight;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><hr><h3 id="adjustment变量计算"><a href="#adjustment变量计算" class="headerlink" title="adjustment变量计算"></a>adjustment变量计算</h3><p>例如dump我的一加六（Android 10）设备，<code>adb shell dumpsys display</code>信息：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">&#x2F;&#x2F;开启自动亮度调节后dump</span><br><span class="line">PhysicalMappingStrategy</span><br><span class="line">  mConfig&#x3D;BrightnessConfiguration&#123;[(0.0, 2.0487), (1.0, 4.848), (4.0, 17.2347), (12.0, 39.0867), (20.0, 50.671), (40.0, 92.3019), (65.0, 94.2512), (95.0, 98.372), (140.0, 100.297), (200.0, 105.297), (350.0, 120.385), (650.0, 142.064), (1300.0, 236.5179), (2000.0, 350.0267), (3300.0, 427.6287)], &#123;&#125;, &#39;&#39;&#125;</span><br><span class="line">  &#x2F;&#x2F;背光曲线</span><br><span class="line">  mBrightnessSpline&#x3D;MonotoneCubicSpline&#123;[(0.0, 2.0487: 2.7699974), (1.0, 4.8186975: 3.4543567), (4.0, 17.234846: 3.435018), (12.0, 39.085403: 2.089745), (20.0, 50.67077: 1.7645122), (40.0, 92.28784: 0.23451348), (65.0, 94.25191: 0.023499148), (95.0, 98.38648: 0.09019067), (140.0, 100.30178: 0.0627253), (200.0, 105.275085: 0.0918182), (350.0, 120.38728: 0.08650002), (650.0, 142.0629: 0.108782366), (1300.0, 236.51614: 0.15355834), (2000.0, 349.77896: 0.11084422), (3300.0, 427.6287: 0.05988441)]&#125;</span><br><span class="line">  &#x2F;&#x2F;nits对应背光的曲线</span><br><span class="line">  mNitsToBacklightSpline&#x3D;MonotoneCubicSpline&#123;[(2.0482, 0.0019550342: 0.00395116), (2.543, 0.0039100684: 0.004002362), (3.0253, 0.0058651026: 0.0040531447), (3.5077, 0.007820137: 0.0037272798), (4.0824, 0.009775171: 0.00419204), (4.4748, 0.011730205: 0.005721517), (5.08, 0.015640274: 0.004685791), (6.4233, 0.019550342: 0.002632065), (8.0848, 0.02346041: 0.0017233933), (11.6607, 0.027370479: 0.001788805), (13.2347, 0.031280547: 0.0023087142), (15.0676, 0.035190616: 0.0021758107), (16.8302, 0.039100684: 0.0023342122), (18.4261, 0.043010753: 0.002262629), (20.3103, 0.04692082: 0.0022641667), (21.9042, 0.05083089: 0.00241765), (23.5456, 0.054740958: 0.00236309), (25.2137, 0.058651026: 0.0021678535), (27.1769, 0.062561095: 0.0020940518), (28.9571, 0.06647117: 0.0023456002), (30.5244, 0.07038123: 0.0023162398), (32.3535, 0.074291304: 0.0021968414), (34.0867, 0.07820137: 0.0023086662), (42.366, 0.097751714: 0.0022959393), (51.1309, 0.11730205: 0.0022804863), (59.52, 0.1368524: 0.0023538377), (67.744, 0.15640274: 0.002376392), (75.9738, 0.17595308: 0.002316629), (84.6332, 0.19550343: 0.002155731), (94.1525, 0.21505377: 0.002238446), (102.2207, 0.2346041: 0.0023939852), (110.4878, 0.25415444: 0.0026741978), (117.0405, 0.2737048: 0.0028248527), (124.3733, 0.29325512: 0.0028097979), (130.9928, 0.31280547: 0.00251312), (140.4247, 0.33235583: 0.0021358524), (149.3156, 0.35190615: 0.0023393487), (157.1995, 0.3714565: 0.0024370078), (165.3651, 0.39100686: 0.0024333047), (173.2726, 0.41055718: 0.0024349196), (181.4272, 0.43010753: 0.0024660935), (189.1402, 0.44965786: 0.0024320162), (197.5334, 0.4692082: 0.0023719585), (205.6301, 0.48875856: 0.002383901), (213.9381, 0.5083089: 0.002348846), (222.2769, 0.5278592: 0.002423523), (230.0891, 0.5474096: 0.002398687), (238.6084, 0.5669599: 0.002379861), (246.5399, 0.58651024: 0.0023049354), (255.6544, 0.6060606: 0.0022993367), (263.6221, 0.62561095: 0.002403119), (271.9324, 0.6451613: 0.002531584), (279.1449, 0.66471165: 0.0023920578), (288.5736, 0.684262: 0.002112214), (297.6628, 0.7038123: 0.0022218374), (306.1899, 0.7233627: 0.0023296294), (314.4511, 0.742913: 0.0024545297), (322.1404, 0.76246333: 0.002378489), (330.969, 0.7820137: 0.002454385), (338.2251, 0.80156404: 0.0025690594), (346.2251, 0.82111436: 0.0023937116), (354.567, 0.8406647: 0.0017740328), (370.799, 0.86021507: 2.6955837E-4), (413.1738, 0.8797654: 0.0013575983), (415.6397, 0.8993157: 0.009982219), (417.264, 0.9188661: 0.010905683), (419.264, 0.9384164: 0.009775162), (421.264, 0.95796674: 0.007777949), (424.646, 0.9775171: 0.0066592516), (427.6287, 1.0: 0.0075377673)]&#125;</span><br><span class="line"></span><br><span class="line">  mMaxGamma&#x3D;3.0</span><br><span class="line">  mAutoBrightnessAdjustment&#x3D;0.0</span><br><span class="line">  mUserLux&#x3D;-1.0</span><br><span class="line">  mUserBrightness&#x3D;-1.0</span><br><span class="line">  mDefaultConfig&#x3D;BrightnessConfiguration&#123;[(0.0, 2.0487), (1.0, 4.848), (4.0, 17.2347), (12.0, 39.0867), (20.0, 50.671), (40.0, 92.3019), (65.0, 94.2512), (95.0, 98.372), (140.0, 100.297), (200.0, 105.297), (350.0, 120.385), (650.0, 142.064), (1300.0, 236.5179), (2000.0, 350.0267), (3300.0, 427.6287)], &#123;&#125;, &#39;&#39;&#125;</span><br></pre></td></tr></table></figure><h4 id="用户手动调节亮度条"><a href="#用户手动调节亮度条" class="headerlink" title="用户手动调节亮度条"></a>用户手动调节亮度条</h4><p>入参adjustment，范围是[-1.0, 1.0]</p><p>DisplayManager的setTemporaryAutoBrightnessAdjustment接口供上层应用调用，作为入口</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></pre></td><td class="code"><pre><span class="line">frameworks&#x2F;base&#x2F;core&#x2F;java&#x2F;android&#x2F;hardware&#x2F;display&#x2F;DisplayManager.java - setTemporaryAutoBrightnessAdjustment  ----&gt;</span><br><span class="line">frameworks&#x2F;base&#x2F;core&#x2F;java&#x2F;android&#x2F;hardware&#x2F;display&#x2F;DisplayManagerGlobal.java - setTemporaryAutoBrightnessAdjustment [入参adjustment]  ----&gt;</span><br><span class="line">IDisplayManager.aidl - setTemporaryAutoBrightnessAdjustment  [AIDL跨进程]  ----&gt;</span><br><span class="line">DisplayManagerService.java - setTemporaryAutoBrightnessAdjustment [Binder call] ---&gt;</span><br><span class="line">DisplayPowerController.java - setTemporaryAutoBrightnessAdjustment [消息处理]  -----&gt;</span><br><span class="line">updatePowerState   ----&gt;</span><br><span class="line">（1） updateUserSetScreenBrightness  [mLastUserSetScreenBrightness更新值，并返回true赋值给userSetBrightnessChanged] 详细代码参考上面小节</span><br><span class="line">（2） AutomaticBrightnessController.configure [会入参上面两个变量：mLastUserSetScreenBrightness和userSetBrightnessChanged]   ----&gt;</span><br><span class="line">setScreenBrightnessByUser   ----&gt;</span><br><span class="line">BrightnessMappingStrategy.java - addUserDataPoint   ----&gt;</span><br><span class="line">getUnadjustedBrightness</span><br></pre></td></tr></table></figure><p>大致流程：<br><img src="userChangeBrightness.png" alt="流程"></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></pre></td><td class="code"><pre><span class="line"><span class="comment">//DisplayPowerController.java - updatePowerState</span></span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">if</span> (mAutomaticBrightnessController != <span class="keyword">null</span>) &#123;</span><br><span class="line">            hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();</span><br><span class="line">            mAutomaticBrightnessController.configure(autoBrightnessEnabled,</span><br><span class="line">                    mBrightnessConfiguration,</span><br><span class="line">                    mLastUserSetScreenBrightness,</span><br><span class="line">                    userSetBrightnessChanged, autoBrightnessAdjustment,</span><br><span class="line">                    autoBrightnessAdjustmentChanged, mPowerRequest.policy);</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//============================</span></span><br><span class="line"><span class="comment">//AutomaticBrightnessController.java</span></span><br><span class="line"><span class="comment">//mLastUserSetScreenBrightness对应brightness</span></span><br><span class="line"><span class="comment">//userSetBrightnessChanged对应userChangedBrightness</span></span><br><span class="line"><span class="comment">//autoBrightnessAdjustment对应adjustment</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(<span class="keyword">boolean</span> enable, @Nullable BrightnessConfiguration configuration,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">float</span> brightness, <span class="keyword">boolean</span> userChangedBrightness, <span class="keyword">float</span> adjustment,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">boolean</span> userChangedAutoBrightnessAdjustment, <span class="keyword">int</span> displayPolicy)</span> </span>&#123;</span><br><span class="line">                ....</span><br><span class="line">        <span class="keyword">if</span> (userChangedBrightness &amp;&amp; enable) &#123;</span><br><span class="line">            <span class="comment">// Update the brightness curve with the new user control point. It's critical this</span></span><br><span class="line">            <span class="comment">// happens after we update the autobrightness adjustment since it may reset it.</span></span><br><span class="line">            changed |= setScreenBrightnessByUser(brightness);</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">//BrightnessMappingStrategy.java</span></span><br><span class="line"><span class="comment">//brightness即上面传参的用户手动设置的背光mLastUserSetScreenBrightness</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">addUserDataPoint</span><span class="params">(<span class="keyword">float</span> lux, <span class="keyword">float</span> brightness)</span> </span>&#123;</span><br><span class="line">            <span class="comment">//step 1： 获取默认配置值创建的曲线中Lux值对应的背光值</span></span><br><span class="line">            <span class="keyword">float</span> unadjustedBrightness = getUnadjustedBrightness(lux);</span><br><span class="line">            <span class="comment">//step 2： 通过传入的三个值计算调整 自动背光值</span></span><br><span class="line">            <span class="keyword">float</span> adjustment = inferAutoBrightnessAdjustment(mMaxGamma,</span><br><span class="line">                    brightness <span class="comment">/* desiredBrightness */</span>,</span><br><span class="line">                    unadjustedBrightness <span class="comment">/* currentBrightness */</span>);</span><br><span class="line">            <span class="comment">//更新自动背光调整值（浮点）</span></span><br><span class="line">            mAutoBrightnessAdjustment = adjustment;</span><br><span class="line">            <span class="comment">//表示用户在开启自动背光情况下拖动亮度条调节亮度时的当时Lux值</span></span><br><span class="line">            mUserLux = lux;</span><br><span class="line">            <span class="comment">//表示用户在开启自动背光情况下拖动亮度条调节亮度值</span></span><br><span class="line">            mUserBrightness = brightness;</span><br><span class="line">            <span class="comment">//step 3： 更新曲线图</span></span><br><span class="line">            computeSpline();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//step 1 获取曲线中的对应背光值</span></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">float</span> <span class="title">getUnadjustedBrightness</span><span class="params">(<span class="keyword">float</span> lux)</span> </span>&#123;</span><br><span class="line">            <span class="comment">//获取BrightnessConfiguration中的Lux-Nits映射</span></span><br><span class="line">            Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; curve = mConfig.getCurve();</span><br><span class="line">            <span class="comment">//创建一个lux-nits样条曲线</span></span><br><span class="line">            Spline spline = Spline.createSpline(curve.first, curve.second);</span><br><span class="line">            <span class="comment">//根据lux得到nits值，再根据nit值从mNitsToBacklightSpline中的到对应的背光 0-1的float浮点数</span></span><br><span class="line">            <span class="keyword">return</span> mNitsToBacklightSpline.interpolate(spline.interpolate(lux));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//step 2 推断adjustment值</span></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">float</span> <span class="title">inferAutoBrightnessAdjustment</span><span class="params">(<span class="keyword">float</span> maxGamma, <span class="keyword">float</span> desiredBrightness,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">float</span> currentBrightness)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">float</span> adjustment = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">float</span> gamma = Float.NaN;</span><br><span class="line">        <span class="comment">// 当前Lux值对应的默认配置亮度值 &lt;=25.5 || &gt;= 229.5  (0-255默认背光)</span></span><br><span class="line">        <span class="keyword">if</span> (currentBrightness &lt;= <span class="number">0.1f</span> || currentBrightness &gt;= <span class="number">0.9f</span>) &#123;</span><br><span class="line">            adjustment = (desiredBrightness - currentBrightness);</span><br><span class="line">        <span class="comment">//最暗的可能情况</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (desiredBrightness == <span class="number">0</span>) &#123;</span><br><span class="line">            adjustment = -<span class="number">1</span>;</span><br><span class="line">        <span class="comment">//最大亮度255</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (desiredBrightness == <span class="number">1</span>) &#123;</span><br><span class="line">            adjustment = +<span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// current^gamma = desired =&gt; gamma = log[current](desired)</span></span><br><span class="line">            <span class="comment">//根据换底公式，得到currentBrightness为底desiredBrightness的对数</span></span><br><span class="line">            gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness);</span><br><span class="line">            <span class="comment">// max^-adjustment = gamma =&gt; adjustment = -log[max](gamma)</span></span><br><span class="line">            <span class="comment">//maxGamma为底gamma的对数</span></span><br><span class="line">            adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);</span><br><span class="line">        &#125;</span><br><span class="line">        adjustment = MathUtils.constrain(adjustment, -<span class="number">1</span>, +<span class="number">1</span>);</span><br><span class="line">        <span class="comment">//返回计算后的adjustment</span></span><br><span class="line">        <span class="keyword">return</span> adjustment;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//step 3 在computeSpline方法重新创建样条曲线的时候，会调用getAdjustedCurve函数</span></span><br><span class="line">    <span class="keyword">protected</span> Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; getAdjustedCurve(<span class="keyword">float</span>[] lux, <span class="keyword">float</span>[] brightness,</span><br><span class="line">            <span class="keyword">float</span> userLux, <span class="keyword">float</span> userBrightness, <span class="keyword">float</span> adjustment, <span class="keyword">float</span> maxGamma) &#123;</span><br><span class="line">        <span class="keyword">float</span>[] newLux = lux;</span><br><span class="line">        <span class="keyword">float</span>[] newBrightness = Arrays.copyOf(brightness, brightness.length);</span><br><span class="line">        <span class="comment">//限定取值范围</span></span><br><span class="line">        adjustment = MathUtils.constrain(adjustment, -<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">        <span class="comment">//maxGamma 的 -adjustment次方得到gamma值</span></span><br><span class="line">        <span class="keyword">float</span> gamma = MathUtils.pow(maxGamma, -adjustment);</span><br><span class="line">        <span class="comment">//gamma != 1说明adjustment不为0</span></span><br><span class="line">        <span class="keyword">if</span> (gamma != <span class="number">1</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; newBrightness.length; i++) &#123;</span><br><span class="line">                <span class="comment">//重新设置亮度值，新的亮度值为就亮度值的gamma次方</span></span><br><span class="line">                newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//用户在BrightnessController拖动条上手动调节了亮度</span></span><br><span class="line">        <span class="keyword">if</span> (userLux != -<span class="number">1</span>) &#123;</span><br><span class="line">            <span class="comment">//插入一对新的lux和对应背光值</span></span><br><span class="line">            Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; curve = insertControlPoint(newLux, newBrightness, userLux,</span><br><span class="line">                    userBrightness);</span><br><span class="line">            newLux = curve.first;</span><br><span class="line">            newBrightness = curve.second;</span><br><span class="line">            <span class="keyword">if</span> (mLoggingEnabled) &#123;</span><br><span class="line">                <span class="comment">// This is done for comparison.</span></span><br><span class="line">                curve = insertControlPoint(lux, brightness, userLux, userBrightness);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//返回该Pair对象给到computeSpline()中</span></span><br><span class="line">        <span class="keyword">return</span> Pair.create(newLux, newBrightness);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Pair&lt;<span class="keyword">float</span>[], <span class="keyword">float</span>[]&gt; insertControlPoint(</span><br><span class="line">            <span class="keyword">float</span>[] luxLevels, <span class="keyword">float</span>[] brightnessLevels, <span class="keyword">float</span> lux, <span class="keyword">float</span> brightness) &#123;</span><br><span class="line">        <span class="comment">//找到插入点索引，以保持值的递增</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> idx = findInsertionPoint(luxLevels, lux);</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span>[] newLuxLevels;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">float</span>[] newBrightnessLevels;</span><br><span class="line">        <span class="comment">//1. 插入索引等于数组长度</span></span><br><span class="line">        <span class="keyword">if</span> (idx == luxLevels.length) &#123;</span><br><span class="line">            <span class="comment">//需要插入到末尾位置，且新Lux数组长度比原来大1</span></span><br><span class="line">            newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + <span class="number">1</span>);</span><br><span class="line">            newBrightnessLevels  = Arrays.copyOf(brightnessLevels, brightnessLevels.length + <span class="number">1</span>);</span><br><span class="line">            newLuxLevels[idx] = lux;</span><br><span class="line">            newBrightnessLevels[idx] = brightness;</span><br><span class="line">        <span class="comment">//2. 用户lux值已处在lux值数组中</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (luxLevels[idx] == lux) &#123;</span><br><span class="line">            newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length);</span><br><span class="line">            newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length);</span><br><span class="line">            newBrightnessLevels[idx] = brightness;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//初始化新的lux数组，长度比原数组大1</span></span><br><span class="line">            newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + <span class="number">1</span>);</span><br><span class="line">            <span class="comment">//将原数组从idx位置起的元素，向后移动1位，将新的Lux值插入到idx位置</span></span><br><span class="line">            System.arraycopy(newLuxLevels, idx, newLuxLevels, idx+<span class="number">1</span>, luxLevels.length - idx);</span><br><span class="line">            newLuxLevels[idx] = lux;</span><br><span class="line">            <span class="comment">//初始化新亮度值数组，长度比原数组大1</span></span><br><span class="line">            newBrightnessLevels  = Arrays.copyOf(brightnessLevels, brightnessLevels.length + <span class="number">1</span>);</span><br><span class="line">            <span class="comment">//原数组从idx位置起的元素，向后移动1位，将新的亮度值值插入到idx位置</span></span><br><span class="line">            System.arraycopy(newBrightnessLevels, idx, newBrightnessLevels, idx+<span class="number">1</span>,</span><br><span class="line">                    brightnessLevels.length - idx);</span><br><span class="line">            newBrightnessLevels[idx] = brightness;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//平滑曲线</span></span><br><span class="line">        <span class="comment">//newLuxLevels之后的Lux对应的亮度值将全部为newBrightnessLevels</span></span><br><span class="line">        smoothCurve(newLuxLevels, newBrightnessLevels, idx);</span><br><span class="line">        <span class="comment">//返回一个携带有Lux和背光值的Pair对象给getAdjustedCurve()</span></span><br><span class="line">        <span class="keyword">return</span> Pair.create(newLuxLevels, newBrightnessLevels);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//返回arr数组（递增）中开始大于等于val值的 数组下标</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">findInsertionPoint</span><span class="params">(<span class="keyword">float</span>[] arr, <span class="keyword">float</span> val)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (val &lt;= arr[i]) &#123;</span><br><span class="line">                <span class="keyword">return</span> i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> arr.length;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><hr><h2 id="debug调试"><a href="#debug调试" class="headerlink" title="debug调试"></a>debug调试</h2><ol><li>dump power：执行命令<code>adb shell dumpsys power|grep &quot;Brightness&quot;</code>查看背光相关</li><li>抓取log查看上层背光变化，背光节点，以及kernel中背光变化</li><li>在DisplayPowerController.java中将Debug log开关打开</li><li>使用dump查看自动亮度调节的变量值: <code>adb shell dumpsys display</code></li></ol><hr><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="http://aosp.opersys.com/xref/android-11.0.0_r26/" target="_blank" rel="noopener">Android R AOSP源码Opensys</a></p><p><a href="https://blog.csdn.net/liuhe688/article/details/6532519" target="_blank" rel="noopener">详解Android中AsyncTask的使用</a></p><p><a href="https://www.cnblogs.com/absfree/p/5357678.html" target="_blank" rel="noopener">深入理解AsyncTask的工作原理</a></p><p><a href="https://blog.csdn.net/u012975705/article/details/48632587" target="_blank" rel="noopener">Android Message和obtainMessage的区别</a></p><p><a href="https://www.cnblogs.com/android007/archive/2012/05/10/2494766.html" target="_blank" rel="noopener">Handler的sendMessage和obtainMessage和sendToTarget比较</a></p><p><a href="https://blog.csdn.net/kitty_landon/article/details/64128140" target="_blank" rel="noopener">Android7.1 亮度自动调节</a></p><p>*<a href="https://blog.csdn.net/FightFightFight/article/details/85797336" target="_blank" rel="noopener">Android 9.0 自动背光机制分析</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;参考Android R源码，对背光手动调节、自动调节的流程作简单梳理。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android R Vsync相关梳理</title>
    <link href="https://alonealive.github.io/Blog/2021/04/14/2021/210414_android_VsyncStudy/"/>
    <id>https://alonealive.github.io/Blog/2021/04/14/2021/210414_android_VsyncStudy/</id>
    <published>2021-04-14T12:55:00.000Z</published>
    <updated>2021-05-27T14:44:23.568Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>为了理解systrace中HW Vsync， sf vsync， app vsync的含义和作用。这里主要参照Android R AOSP源码对这几种VSYNC的关系和调用流程进行大致的梳理。</p></blockquote><a id="more"></a><h2 id="vsync相关线程"><a href="#vsync相关线程" class="headerlink" title="vsync相关线程"></a>vsync相关线程</h2><ul><li>EventControlThread: 控制硬件vsync的开关</li><li>DispSyncThread: 软件产生vsync的线程，接收HWComposer HAL的VSYNC信号，并分发给EventThread</li><li>SF EventThread: 该线程用于SurfaceFlinger接收vsync信号用于渲染</li><li>App EventThread: 该线程用于接收vsync信号并且上报给App进程，App开始绘制</li></ul><h3 id="四种vsync"><a href="#四种vsync" class="headerlink" title="四种vsync"></a>四种vsync</h3><p>从这4个线程，可以将vsync分为4种不同的类型：</p><ol><li>HW vsync, 真实由硬件产生的vsync信号</li><li>SW vsync, 由DispSync产生的vsync信号</li><li>SF vsync, SF接收到的vsync信号</li><li>App vsync, App接收到的vsync信号</li></ol><p><img src="VSYNC_addResyncSample.png" alt="4个vsync信号之间的关系"></p><h3 id="硬件vsync"><a href="#硬件vsync" class="headerlink" title="硬件vsync"></a>硬件vsync</h3><p>HWComposer HAL通过callback函数，把VSYNC信号传给DispSyncThread，DispSyncThread传给EventThread</p><p><img src="sf_vsyncThread.jpeg" alt="vysnc相关线程"></p><h3 id="函数调用总流程图"><a href="#函数调用总流程图" class="headerlink" title="函数调用总流程图"></a>函数调用总流程图</h3><p><img src="Display_Vsync_Program.png" alt="完整流程图"></p><p>部分流程：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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">SurfaceFlinger::init() ----&gt;</span><br><span class="line">SurfaceFlinger::processDisplayHotplugEventsLocked()  ----&gt;</span><br><span class="line">SurfaceFlinger::initScheduler 创建sf thread和app thread   ----&gt;</span><br><span class="line">(1)Scheduler::createConnection   ----&gt;</span><br><span class="line">Scheduler::createConnectionInternal   ----&gt;</span><br><span class="line">EventThread::createEventConnection [new EventThreadConnection创建对象]</span><br><span class="line"></span><br><span class="line">(2)MessageQueue::setEventConnection   ----&gt;</span><br><span class="line">(2.1) mEventTube.getFd()</span><br><span class="line">(2.2) stealReceiveChannel</span><br><span class="line">(2.3) MessageQueue::cb_eventReceiver ----&gt;</span><br><span class="line">MessageQueue::eventReceiver  [接收Vsync信号]   ----&gt;</span><br><span class="line">MessageQueue::Handler::dispatchInvalidate   [Handler消息处理]----&gt;</span><br><span class="line">MessageQueue::Handler::handleMessage   ----&gt;</span><br><span class="line">SurfaceFlinger::onMessageReceived   ----&gt;</span><br><span class="line">SurfaceFlinger::onMessageInvalidate    -----&gt;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;合成刷新是通过frameAvailableListener-&gt;onFrameAvailable(item)触发</span><br><span class="line">SurfaceFlinger::signalLayerUpdate()  请求合成 ----&gt;</span><br><span class="line">MessageQueue::invalidate()   -----&gt;</span><br><span class="line">EventThreadConnection::requestNextVsync()</span><br></pre></td></tr></table></figure><h3 id="SF创建EventThread-app-amp-sf"><a href="#SF创建EventThread-app-amp-sf" class="headerlink" title="SF创建EventThread(app&amp;sf)"></a>SF创建EventThread(app&amp;sf)</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//函数SurfaceFlinger::initScheduler</span></span><br><span class="line">    <span class="comment">// start the EventThread，调用setVsyncEnabled函数控制硬件Vysnc</span></span><br><span class="line">    mScheduler =</span><br><span class="line">            getFactory().createScheduler([<span class="keyword">this</span>](<span class="keyword">bool</span> enabled) &#123; setVsyncEnabled(enabled); &#125;,   </span><br><span class="line">                                         *mRefreshRateConfigs, *<span class="keyword">this</span>);</span><br><span class="line">    mAppConnectionHandle =</span><br><span class="line">            mScheduler-&gt;createConnection(<span class="string">"app"</span>, mPhaseConfiguration-&gt;getCurrentOffsets().late.app,</span><br><span class="line">                                         impl::EventThread::InterceptVSyncsCallback());</span><br><span class="line">    mSfConnectionHandle =</span><br><span class="line">            mScheduler-&gt;createConnection(<span class="string">"sf"</span>, mPhaseConfiguration-&gt;getCurrentOffsets().late.sf,</span><br><span class="line">                                         [<span class="keyword">this</span>](<span class="keyword">nsecs_t</span> timestamp) &#123;</span><br><span class="line">                                             mInterceptor-&gt;saveVSyncEvent(timestamp);</span><br><span class="line">                                         &#125;);</span><br></pre></td></tr></table></figure><h3 id="SF注册Connection"><a href="#SF注册Connection" class="headerlink" title="SF注册Connection"></a>SF注册Connection</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></span><br><span class="line">EventThreadConnection::EventThreadConnection(EventThread* eventThread,</span><br><span class="line">                                             ResyncCallback resyncCallback,</span><br><span class="line">                                             ISurfaceComposer::ConfigChanged configChanged)</span><br><span class="line">      : resyncCallback(<span class="built_in">std</span>::move(resyncCallback)),</span><br><span class="line">        mConfigChanged(configChanged),</span><br><span class="line">        mEventThread(eventThread),</span><br><span class="line">        mChannel(gui::BitTube(<span class="number">8</span> * <span class="number">1024</span> <span class="comment">/* default size is 4KB, double it */</span>)) &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EventThreadConnection::onFirstRef</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// <span class="doctag">NOTE:</span> mEventThread doesn't hold a strong reference on us</span></span><br><span class="line">    mEventThread-&gt;registerDisplayEventConnection(<span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">EventThread::registerDisplayEventConnection</span><span class="params">(<span class="keyword">const</span> sp&lt;EventThreadConnection&gt;&amp; connection)</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// this should never happen</span></span><br><span class="line">    <span class="keyword">auto</span> it = <span class="built_in">std</span>::find(mDisplayEventConnections.cbegin(),</span><br><span class="line">            mDisplayEventConnections.cend(), connection);</span><br><span class="line">    <span class="keyword">if</span> (it != mDisplayEventConnections.cend()) &#123;</span><br><span class="line">        ALOGW(<span class="string">"DisplayEventConnection %p already exists"</span>, connection.get());</span><br><span class="line">        mCondition.notify_all();</span><br><span class="line">        <span class="keyword">return</span> ALREADY_EXISTS;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//添加到mDisplayEventConnections集合中</span></span><br><span class="line">    mDisplayEventConnections.push_back(connection);</span><br><span class="line">    <span class="comment">//唤醒threadMain函数（唤醒所有等待队列中阻塞的线程，存在锁争用，只有一个线程能够获得锁）</span></span><br><span class="line">    mCondition.notify_all();</span><br><span class="line">    <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Note：</strong> 有关<code>notify_all</code>和<code>unique_lock&lt;std::mutex&gt;</code>锁可参考：<a href="https://blog.csdn.net/feikudai8460/article/details/109604690" target="_blank" rel="noopener">C++11条件变量：notify_one()与notify_all()的区别</a></p><hr><h2 id="sf请求vsync"><a href="#sf请求vsync" class="headerlink" title="sf请求vsync"></a>sf请求vsync</h2><p><strong>两条请求vsync的流程：</strong></p><ol><li><p>参考<a href="https://wizzie.top/Blog/2020/10/15/2020/201015_android_SurfaceFlinger1/#signalLayerUpdate%E9%80%9A%E7%9F%A5Layer%E6%9B%B4%E6%96%B0%E4%BF%A1%E6%81%AF" target="_blank" rel="noopener">signalLayerUpdate通知Layer更新信息</a>开始，SF触发合成开始请求VSYNC</p></li><li><p><strong>当显示屏准备完毕，SF EventThread connection开始监听Vsync信号，相关流程：</strong></p></li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">SurfaceFlinger::init()   ----&gt;</span><br><span class="line">SurfaceFlinger::initializeDisplays()  设置初始条件 -----&gt;</span><br><span class="line">SurfaceFlinger::onInitializeDisplays()  -----&gt;</span><br><span class="line">SurfaceFlinger::setTransactionState   ----&gt;</span><br><span class="line">SurfaceFlinger::setTransactionFlags   ----&gt;</span><br><span class="line">SurfaceFlinger::signalTransaction()   ----&gt;</span><br><span class="line">MessageQueue::invalidate()   [在请求刷新合成时也会调用] -----&gt;</span><br><span class="line">EventThread::requestNextVsync</span><br></pre></td></tr></table></figure><h3 id="EventThread线程唤醒函数threadmain"><a href="#EventThread线程唤醒函数threadmain" class="headerlink" title="EventThread线程唤醒函数threadmain"></a>EventThread线程唤醒函数threadmain</h3><p>部分代码：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EventThread::requestNextVsync</span><span class="params">(<span class="keyword">const</span> sp&lt;EventThreadConnection&gt;&amp; connection)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (connection-&gt;resyncCallback) &#123;</span><br><span class="line">        connection-&gt;resyncCallback();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (connection-&gt;vsyncRequest == VSyncRequest::None) &#123;   <span class="comment">//None=-1</span></span><br><span class="line">        connection-&gt;vsyncRequest = VSyncRequest::Single;   <span class="comment">//Single=0，只接收一次信号</span></span><br><span class="line">        mCondition.notify_all();  <span class="comment">//唤醒threadmain函数</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>在threadmain函数其中创建了<strong>DisplayEventReceiver</strong>对象，该类用于传输VSYNC信号。</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">DisplayEventReceiver::DisplayEventReceiver  (MessageQueue::setEventConnection) ----&gt;</span><br><span class="line">EventThreadConnection::stealReceiveChannel  -----&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">//函数MessageQueue::setEventConnection也会调用到此处</span></span><br><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">EventThreadConnection::stealReceiveChannel</span><span class="params">(gui::BitTube* outChannel)</span> </span>&#123;</span><br><span class="line">    outChannel-&gt;setReceiveFd(mChannel.moveReceiveFd());</span><br><span class="line">    <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>调用DispSyncSource.setVSyncEnabled开启硬件Vsync</li></ol><h3 id="BitTube"><a href="#BitTube" class="headerlink" title="BitTube"></a>BitTube</h3><p>参考：<a href="https://wizzie.top/Blog/2020/10/15/2020/201015_android_SurfaceFlinger1/#setEventThread%E5%8F%98%E6%9B%B4" target="_blank" rel="noopener">setEventThread变更</a></p><p>BitTube，其实现是socketpairt套接字，用于传递消息。Buffer大小是4K</p><h3 id="DisplayEventReceiver"><a href="#DisplayEventReceiver" class="headerlink" title="DisplayEventReceiver"></a>DisplayEventReceiver</h3><p>查看该类的头文件，关于vsync的主要函数作用：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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">//frameworks/native/libs/gui/include/gui/DisplayEventReceiver.h</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">//DisplayEventReceiver创建并注册了SF的一个事件连接，默认禁止VSync</span></span><br><span class="line">    <span class="comment">//通过调用setVSyncRate、requestNextVsync开始接受。其他事件则即刻分发</span></span><br><span class="line">    <span class="function"><span class="keyword">explicit</span> <span class="title">DisplayEventReceiver</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,</span></span></span><br><span class="line"><span class="function"><span class="params">            ISurfaceComposer::ConfigChanged configChanged =</span></span></span><br><span class="line"><span class="function"><span class="params">                    ISurfaceComposer::eConfigChangedSuppress)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//获取用于接收事件的文件描述符，该描述符由本类持有，不得关闭</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">getFd</span><span class="params">()</span> <span class="keyword">const</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//从队列中读取事件并返回事件个数如果返回NOT_ENOUGH_DATA</span></span><br><span class="line">    <span class="comment">//如果返回的数据不够多，则对象将永远无效，应该销毁，并且不应该再次调用getEvents</span></span><br><span class="line">    <span class="function"><span class="keyword">ssize_t</span> <span class="title">getEvents</span><span class="params">(Event* events, <span class="keyword">size_t</span> count)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">ssize_t</span> <span class="title">getEvents</span><span class="params">(gui::BitTube* dataChannel, Event* events, <span class="keyword">size_t</span> count)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//向队列中写入事件并返回写入的数量</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">ssize_t</span> <span class="title">sendEvents</span><span class="params">(gui::BitTube* dataChannel, Event <span class="keyword">const</span>* events, <span class="keyword">size_t</span> count)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//设置VSync分发频率，每次VSync事件返回1，其他事件返回2，没有事件返回0</span></span><br><span class="line">    <span class="function"><span class="keyword">status_t</span> <span class="title">setVsyncRate</span><span class="params">(<span class="keyword">uint32_t</span> count)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//请求下一次Vsync</span></span><br><span class="line">    <span class="function"><span class="keyword">status_t</span> <span class="title">requestNextVsync</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//强制请求当前primary display的config属性</span></span><br><span class="line">    <span class="function"><span class="keyword">status_t</span> <span class="title">requestLatestConfig</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    sp&lt;IDisplayEventConnection&gt; mEventConnection;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;gui::BitTube&gt; mDataChannel;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="DispSyncSource-setVSyncEnabled开启硬件Vsync"><a href="#DispSyncSource-setVSyncEnabled开启硬件Vsync" class="headerlink" title="DispSyncSource.setVSyncEnabled开启硬件Vsync"></a>DispSyncSource.setVSyncEnabled开启硬件Vsync</h3><p>流程：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">EventThread::threadMain   -----&gt;</span><br><span class="line">DispSyncSource::setVSyncEnabled   ----&gt;</span><br><span class="line">surfaceflinger&#x2F;Scheduler&#x2F;DispSync.cpp  -- DispSync::addEventListener   ----&gt;</span><br><span class="line">class DispSyncThread : public Thread  -- addEventListener</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><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">//surfaceflinger/Scheduler/EventThread.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EventThread::threadMain</span><span class="params">(<span class="built_in">std</span>::unique_lock&lt;<span class="built_in">std</span>::mutex&gt;&amp; lock)</span> </span>&#123;</span><br><span class="line">    .....</span><br><span class="line">        State nextState;</span><br><span class="line">        <span class="keyword">if</span> (mVSyncState &amp;&amp; vsyncRequested) &#123;</span><br><span class="line">            nextState = mVSyncState-&gt;synthetic ? State::SyntheticVSync : State::VSync;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            ALOGW_IF(!mVSyncState, <span class="string">"Ignoring VSYNC request while display is disconnected"</span>);</span><br><span class="line">            nextState = State::Idle;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mState != nextState) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mState == State::VSync) &#123;</span><br><span class="line">                mVSyncSource-&gt;setVSyncEnabled(<span class="literal">false</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nextState == State::VSync) &#123;</span><br><span class="line">                mVSyncSource-&gt;setVSyncEnabled(<span class="literal">true</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            mState = nextState;</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="comment">//surfaceflinger/Scheduler/DispSyncSource.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DispSyncSource::setVSyncEnabled</span><span class="params">(<span class="keyword">bool</span> enable)</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard <span class="title">lock</span><span class="params">(mVsyncMutex)</span></span>;</span><br><span class="line">    <span class="keyword">if</span> (enable) &#123;</span><br><span class="line">        <span class="comment">//开启硬件Vsync信号就是添加EventListener</span></span><br><span class="line">        <span class="keyword">status_t</span> err = mDispSync-&gt;addEventListener(mName, mPhaseOffset,</span><br><span class="line">                                                   <span class="keyword">static_cast</span>&lt;DispSync::Callback*&gt;(<span class="keyword">this</span>),</span><br><span class="line">                                                   mLastCallbackTime);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">status_t</span> err = mDispSync-&gt;removeEventListener(<span class="keyword">static_cast</span>&lt;DispSync::Callback*&gt;(<span class="keyword">this</span>),</span><br><span class="line">                                                      &amp;mLastCallbackTime);</span><br><span class="line">        <span class="keyword">if</span> (mDolphinCheck) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mDolphinCheck(mName)) &#123;</span><br><span class="line">                <span class="keyword">status_t</span> err = mDispSync-&gt;addEventListener(mName, mPhaseOffset,</span><br><span class="line">                                                           <span class="keyword">static_cast</span>&lt;DispSync::Callback*&gt;(<span class="keyword">this</span>),</span><br><span class="line">                                                           mLastCallbackTime);</span><br><span class="line">                <span class="keyword">if</span> (err != NO_ERROR) &#123;</span><br><span class="line">                    ALOGE(<span class="string">"error registering vsync callback: %s (%d)"</span>, strerror(-err), err);</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">    mEnabled = enable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="硬件VSYNC从HWComposer-HAL发到EventThread"><a href="#硬件VSYNC从HWComposer-HAL发到EventThread" class="headerlink" title="硬件VSYNC从HWComposer HAL发到EventThread"></a>硬件VSYNC从HWComposer HAL发到EventThread</h2><p>SF请求合成时关于Vsync的部分流程：</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></pre></td><td class="code"><pre><span class="line">SurfaceFlinger::onMessageInvalidate  ----&gt;</span><br><span class="line">SurfaceFlinger::updateFrameScheduler()  -----&gt;</span><br><span class="line">(1) SurfaceFlinger::getVsyncPeriod()  ----&gt;</span><br><span class="line">surfaceflinger&#x2F;DisplayHardware&#x2F;HWC2.cpp -- Display::getDisplayVsyncPeriod</span><br><span class="line"></span><br><span class="line">(2) Scheduler::resyncToHardwareVsync</span><br><span class="line">(3) DispSync::addResyncSample  -----&gt;</span><br><span class="line">DispSync::updateModelLocked [开始计算更新SW vsync 模型]</span><br></pre></td></tr></table></figure><h3 id="mPeriod-Vsync周期时长值变更流程"><a href="#mPeriod-Vsync周期时长值变更流程" class="headerlink" title="mPeriod Vsync周期时长值变更流程"></a>mPeriod Vsync周期时长值变更流程</h3><p>流程：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">SurfaceFlinger::init()   ----&gt;</span><br><span class="line">SurfaceFlinger::initializeDisplays()  设置初始条件 -----&gt;</span><br><span class="line">SurfaceFlinger::onInitializeDisplays()  -----&gt;</span><br><span class="line">SurfaceFlinger::setPowerModeInternal  设置Display的power mode -----&gt;</span><br><span class="line">(1) Scheduler::onScreenAcquired</span><br><span class="line"></span><br><span class="line">(2) Scheduler::resyncToHardwareVsync   通过硬件Vsync重新设置软件vysnc -----&gt;</span><br><span class="line">Scheduler::setVsyncPeriod   设置vsync周期时长   -----&gt;</span><br><span class="line"></span><br><span class="line">(1) DispSync::setPeriod</span><br><span class="line">(2) DispSync::beginResync()</span><br><span class="line">(3) EventControlThread::setVsyncEnabled 唤醒threadmain线程[如果硬件vsync没有enable,那么就通知EventControlThread去通知硬件enable VSYNC]</span><br></pre></td></tr></table></figure><h3 id="流程图及部分代码"><a href="#流程图及部分代码" class="headerlink" title="流程图及部分代码"></a>流程图及部分代码</h3><p><img src="HWVsynct_To_EventThread.jpeg" alt="硬件VSYNC从HWComposer HAL发到EventThread"></p><h3 id="resyncToHardwareVsync打开硬件VYSNC"><a href="#resyncToHardwareVsync打开硬件VYSNC" class="headerlink" title="resyncToHardwareVsync打开硬件VYSNC"></a>resyncToHardwareVsync打开硬件VYSNC</h3><p>SurfaceFlinger在初始化HWComposer时会默认关闭硬件Vsync信号，这里直接调用eventControl</p><p>而resyncToHardwareVsync则是和硬件VSYNC进行同步，通过调用到setVsyncEnabled开启硬件VSYNC</p><figure class="highlight cpp"><figcaption><span>surfaceflinger/Scheduler/Scheduler.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">Scheduler::resyncToHardwareVsync</span><span class="params">(<span class="keyword">bool</span> makeAvailable, <span class="keyword">nsecs_t</span> period, <span class="keyword">bool</span> force_resync)</span> </span>&#123;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mHWVsyncLock)</span></span>;</span><br><span class="line">        <span class="comment">//表示硬件VSYNC被enable</span></span><br><span class="line">        <span class="keyword">if</span> (makeAvailable) &#123;  </span><br><span class="line">            mHWVsyncAvailable = makeAvailable;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!mHWVsyncAvailable) &#123;</span><br><span class="line">            <span class="comment">// Hardware vsync is not currently available, so abort the resync</span></span><br><span class="line">            <span class="comment">// attempt for now</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">    <span class="keyword">if</span> (period &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    setVsyncPeriod(period, force_resync);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Scheduler::setVsyncPeriod</span><span class="params">(<span class="keyword">nsecs_t</span> period, <span class="keyword">bool</span> force_resync)</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mHWVsyncLock)</span></span>;</span><br><span class="line">    <span class="comment">//设置DispSync模型里period为显示设备的频率</span></span><br><span class="line">    mPrimaryDispSync-&gt;setPeriod(period);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mPrimaryHWVsyncEnabled || force_resync) &#123;</span><br><span class="line">        mPrimaryDispSync-&gt;beginResync();</span><br><span class="line">        <span class="comment">//如果硬件vsync没有enable,那么就通知EventControlThread去通知硬件enable VSYNC，这个和DispSync的setVsyncEnabled是不一样的</span></span><br><span class="line">        mEventControlThread-&gt;setVsyncEnabled(<span class="literal">true</span>);</span><br><span class="line">        mPrimaryHWVsyncEnabled = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>setVsyncEnabled会释放mCond信号，这样在EventControlThread的threadLoop里的mCond会被唤醒去操作硬件Vsync开关</p><h3 id="addResyncSample更新mPeriod"><a href="#addResyncSample更新mPeriod" class="headerlink" title="*addResyncSample更新mPeriod"></a>*addResyncSample更新mPeriod</h3><h4 id="重要变量的含义："><a href="#重要变量的含义：" class="headerlink" title="重要变量的含义："></a>重要变量的含义：</h4><ul><li>硬件vsync样本个数：<code>MIN_RESYNC_SAMPLES_FOR_UPDATE</code>（要6个硬件vsync样本以上才计算，当然样本越多，模型越精确）</li><li>mPeriod：即是显示屏的刷新率，这里mPeriod是根据样本个数去掉一个最大一个最小，算平均</li><li>mPhase：偏移时间，这个相称和具体的SF/APP Thread里固定的相称是不一样的，这个相移是针对 mPeroid的一个偏移</li><li>mModelUpdated：表示是否模型已经更新</li><li>mReferenceTime：第一个硬件Vsync的时间，每次SW vsync计算下一个vsync时间时，都是以该时间作为基准，这样可以减少误差</li></ul><h4 id="Code"><a href="#Code" class="headerlink" title="Code"></a>Code</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//surfaceflinger/Scheduler/DispSync.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DispSync::beginResync</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line">    ALOGV(<span class="string">"[%s] beginResync"</span>, mName);</span><br><span class="line">    resetLocked();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">DispSync::addResyncSample</span><span class="params">(<span class="keyword">nsecs_t</span> timestamp, <span class="built_in">std</span>::optional&lt;<span class="keyword">nsecs_t</span>&gt; <span class="comment">/*hwcVsyncPeriod*/</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                               <span class="keyword">bool</span>* periodFlushed)</span> </span>&#123;</span><br><span class="line">    <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">    ALOGV(<span class="string">"[%s] addResyncSample(%"</span> PRId64 <span class="string">")"</span>, mName, ns2us(timestamp));</span><br><span class="line"></span><br><span class="line">    *periodFlushed = <span class="literal">false</span>;</span><br><span class="line">    <span class="comment">//MAX_RESYNC_SAMPLES=32,即最大只保存32次硬件vysnc时间戳，用于计算SW Vsync模型</span></span><br><span class="line">    <span class="comment">//mNumResyncSamples表示已有硬件Vysnc样本个数</span></span><br><span class="line">    <span class="comment">//mFirstResyncSample用于记录第几个硬件vysnc</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">size_t</span> idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;</span><br><span class="line">    <span class="comment">//mResyncSamples用于记录每个硬件vsync样本的时间戳</span></span><br><span class="line">    mResyncSamples[idx] = timestamp;</span><br><span class="line">    <span class="keyword">if</span> (mNumResyncSamples == <span class="number">0</span>) &#123;</span><br><span class="line">        mPhase = <span class="number">0</span>;</span><br><span class="line">        ALOGV(<span class="string">"[%s] First resync sample: mPeriod = %"</span> PRId64 <span class="string">", mPhase = 0, "</span></span><br><span class="line">              <span class="string">"mReferenceTime = %"</span> PRId64,</span><br><span class="line">              mName, ns2us(mPeriod), ns2us(timestamp));</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mPendingPeriod &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            ....</span><br><span class="line">            <span class="keyword">if</span> (mTraceDetailedInfo) &#123;</span><br><span class="line">                ATRACE_INT(<span class="string">"DispSync:PendingPeriod"</span>, mPendingPeriod);</span><br><span class="line">                ATRACE_INT(<span class="string">"DispSync:IntendedPeriod"</span>, mIntendedPeriod);</span><br><span class="line">            &#125;</span><br><span class="line">            *periodFlushed = <span class="literal">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">// Always update the reference time with the most recent timestamp.</span></span><br><span class="line">    mReferenceTime = timestamp;</span><br><span class="line">    mThread-&gt;updateModel(mPeriod, mPhase, mReferenceTime);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mNumResyncSamples &lt; MAX_RESYNC_SAMPLES) &#123;</span><br><span class="line">        mNumResyncSamples++;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        mFirstResyncSample = (mFirstResyncSample + <span class="number">1</span>) % MAX_RESYNC_SAMPLES;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//开始计算更新SW Vsync模型</span></span><br><span class="line">    updateModelLocked();</span><br><span class="line">    ......</span><br><span class="line">    ALOGV(<span class="string">"[%s] addResyncSample returning %s"</span>, mName, modelLocked ? <span class="string">"locked"</span> : <span class="string">"unlocked"</span>);</span><br><span class="line">    <span class="keyword">if</span> (modelLocked) &#123;</span><br><span class="line">        *periodFlushed = <span class="literal">true</span>;</span><br><span class="line">        mThread-&gt;lockModel();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> !modelLocked;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//计算更新SW Vsync模型，更新完后就会关闭硬件VSYNC信号</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DispSync::updateModelLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//MIN_RESYNC_SAMPLES_FOR_UPDATE=6</span></span><br><span class="line">    <span class="comment">// 如果已经保存了6个以上的 硬件 vsync样本后，就要开始计算 sw vsync模型了</span></span><br><span class="line">    <span class="keyword">if</span> (mNumResyncSamples &gt;= MIN_RESYNC_SAMPLES_FOR_UPDATE) &#123;</span><br><span class="line">        <span class="keyword">nsecs_t</span> durationSum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">nsecs_t</span> minDuration = INT64_MAX;</span><br><span class="line">        <span class="keyword">nsecs_t</span> maxDuration = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">constexpr</span> <span class="keyword">size_t</span> numSamplesSkipped = <span class="number">2</span>;  <span class="comment">//跳过两个，因为可能存在不准确、延迟</span></span><br><span class="line">        <span class="comment">//还记得上面 如果 mNumResyncSamples=0,即第一个硬件vsync时，直接更新SW vsync模型了，所以这里把第一个给去除掉</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">size_t</span> i = numSamplesSkipped; i &lt; mNumResyncSamples; i++) &#123;</span><br><span class="line">            <span class="keyword">size_t</span> idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;</span><br><span class="line">            <span class="keyword">size_t</span> prev = (idx + MAX_RESYNC_SAMPLES - <span class="number">1</span>) % MAX_RESYNC_SAMPLES;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// mResyncSamples[idx] - mResyncSamples[prev] 这个差值就是计算出两个硬件vsync样本之间的时间间隔</span></span><br><span class="line">            <span class="keyword">nsecs_t</span> duration = mResyncSamples[idx] - mResyncSamples[prev];</span><br><span class="line">            <span class="comment">// durationSum 表示保存的所有样本(除去前两个vsync)时间间隔之后，用于后面计算 平均 mPeriod</span></span><br><span class="line">            durationSum += duration;  </span><br><span class="line">            minDuration = min(minDuration, duration);</span><br><span class="line">            maxDuration = max(maxDuration, duration);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 去掉一个最小，一个最大值再来计算平均值，这个平均值就是硬件vsync产生的时间间隔</span></span><br><span class="line">        durationSum -= minDuration + maxDuration;</span><br><span class="line">        <span class="comment">// 减去4</span></span><br><span class="line">        mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//下面计算出模型需要的偏移, 因为现在 mPeriod 算出来的是平均值，所以并不是真的硬件vsync时间间隔就是 mPeriod, 存在着偏移与噪音(这个和样本个数有很大的关系)</span></span><br><span class="line">       <span class="comment">// 即有些样本信号的时间间隔大于平均值，而有些样本时间间隔小于平均值，而这些与 mPriod的差值就是偏移</span></span><br><span class="line">       <span class="comment">// 下面就是要算出这些平均的偏移值</span></span><br><span class="line">        <span class="keyword">double</span> sampleAvgX = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">double</span> sampleAvgY = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">//将硬件vsync的时间间隔换算成对应的度数,即刻度，这里的刻度表示每ns代表多少度 </span></span><br><span class="line">        <span class="comment">// M_PI是圆周率 3.14159265359f，其中2π就是值360度</span></span><br><span class="line">        <span class="keyword">double</span> scale = <span class="number">2.0</span> * M_PI / <span class="keyword">double</span>(mPeriod);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">size_t</span> i = numSamplesSkipped; i &lt; mNumResyncSamples; i++) &#123;</span><br><span class="line">            <span class="keyword">size_t</span> idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;</span><br><span class="line">            <span class="keyword">nsecs_t</span> sample = mResyncSamples[idx] - mReferenceTime;</span><br><span class="line">           <span class="comment">// 这里对mPeriod取余就是相对于mPeriod倍数的偏移值，然后将其转换成对应的度数</span></span><br><span class="line">            <span class="keyword">double</span> samplePhase = <span class="keyword">double</span>(sample % mPeriod) * scale;</span><br><span class="line">            sampleAvgX += <span class="built_in">cos</span>(samplePhase); <span class="comment">//依次累加成 sampleAvgX</span></span><br><span class="line">            sampleAvgY += <span class="built_in">sin</span>(samplePhase); <span class="comment">//依次累加成 sampleAvgY</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获得在x轴与y轴的偏移的平均值</span></span><br><span class="line">        sampleAvgX /= <span class="keyword">double</span>(mNumResyncSamples - numSamplesSkipped);</span><br><span class="line">        sampleAvgY /= <span class="keyword">double</span>(mNumResyncSamples - numSamplesSkipped);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//最后再通过atan2获得最终的相移值</span></span><br><span class="line">        mPhase = <span class="keyword">nsecs_t</span>(<span class="built_in">atan2</span>(sampleAvgY, sampleAvgX) / scale);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果相移偏过了mPeriod的一半，那么重新调整一下</span></span><br><span class="line">        <span class="keyword">if</span> (mPhase &lt; -(mPeriod / <span class="number">2</span>)) &#123;</span><br><span class="line">            mPhase += mPeriod;</span><br><span class="line">            ALOGV(<span class="string">"[%s] Adjusting mPhase -&gt; %"</span> PRId64, mName, ns2us(mPhase));</span><br><span class="line">        &#125;</span><br><span class="line">      <span class="comment">// 将最新的 偏移 mPhase和 vsync时间间隔mPeriod和mReferenceTime更新到SW vsync模型当中</span></span><br><span class="line">        mThread-&gt;updateModel(mPeriod, mPhase, mReferenceTime);</span><br><span class="line">        </span><br><span class="line">      <span class="comment">// 模型更新了</span></span><br><span class="line">        mModelUpdated = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="计算的相移图"><a href="#计算的相移图" class="headerlink" title="计算的相移图"></a>计算的相移图</h4><p><img src="mPeriodCaculate.png" alt="计算模型的相移图图示"></p><h3 id="setVsyncEnabled硬件VSYNC开关控制"><a href="#setVsyncEnabled硬件VSYNC开关控制" class="headerlink" title="setVsyncEnabled硬件VSYNC开关控制"></a>setVsyncEnabled硬件VSYNC开关控制</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">SurfaceFlinger::init() ----&gt;</span><br><span class="line">SurfaceFlinger::processDisplayHotplugEventsLocked()  ----&gt;</span><br><span class="line">SurfaceFlinger::initScheduler 创建sf thread和app thread   ----&gt;</span><br><span class="line">SurfaceFlinger::setVsyncEnabled(bool enabled)  [传入true，开启硬件vsync]  -----&gt;</span><br><span class="line">SurfaceFlinger::setVsyncEnabledInternal(bool enabled)</span><br></pre></td></tr></table></figure><p>硬件vsync相关函数：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::setVsyncEnabledInternal</span><span class="params">(<span class="keyword">bool</span> enabled)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    <span class="function">Mutex::Autolock <span class="title">lockVsync</span><span class="params">(mVsyncLock)</span></span>;</span><br><span class="line">    <span class="comment">//复制</span></span><br><span class="line">    mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> displayId = getInternalDisplayIdLocked();</span><br><span class="line">    <span class="keyword">if</span> (mNextVsyncSource) &#123;</span><br><span class="line">        <span class="comment">// Disable current vsync source before enabling the next source</span></span><br><span class="line">        <span class="keyword">if</span> (mActiveVsyncSource) &#123;</span><br><span class="line">            displayId = mActiveVsyncSource-&gt;getId();</span><br><span class="line">            getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);</span><br><span class="line">        &#125;</span><br><span class="line">        displayId = mNextVsyncSource-&gt;getId();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mActiveVsyncSource) &#123;</span><br><span class="line">        displayId = mActiveVsyncSource-&gt;getId();</span><br><span class="line">    &#125;</span><br><span class="line">    getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);</span><br><span class="line">    <span class="keyword">if</span> (mNextVsyncSource) &#123;</span><br><span class="line">        mActiveVsyncSource = mNextVsyncSource;</span><br><span class="line">        mNextVsyncSource = <span class="literal">NULL</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="DispSync"><a href="#DispSync" class="headerlink" title="DispSync"></a>DispSync</h2><blockquote><p>DispSyncThread是软件产生vsync的线程，也控制硬件VSync信号同步。DispSync是定义在SurfaceFlinger类里的成员变量，因此在初始化 SurfaceFlinger时，就会初始化DispSync。</p></blockquote><h3 id="DispSync初始化"><a href="#DispSync初始化" class="headerlink" title="DispSync初始化"></a>DispSync初始化</h3><p>流程：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SurfaceFlinger::init() ----&gt;</span><br><span class="line">SurfaceFlinger::processDisplayHotplugEventsLocked()  ---&gt;</span><br><span class="line">SurfaceFlinger::initScheduler  -----&gt;</span><br><span class="line">surfaceflinger&#x2F;SurfaceFlingerDefaultFactory.cpp  -- DefaultFactory::createScheduler ---&gt;</span><br><span class="line">Scheduler::Scheduler  [构造函数]  ----&gt;</span><br><span class="line">std::unique_ptr&lt;DispSync&gt; createDispSync [创建DispSync]</span><br></pre></td></tr></table></figure><p>代码：</p><p><strong>Scheduler构造函数：</strong></p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/Scheduler.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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">Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,</span><br><span class="line">                     <span class="keyword">const</span> scheduler::RefreshRateConfigs&amp; refreshRateConfig,</span><br><span class="line">                     ISchedulerCallback&amp; schedulerCallback, <span class="keyword">bool</span> useContentDetectionV2,</span><br><span class="line">                     <span class="keyword">bool</span> useContentDetection)</span><br><span class="line">      : mSupportKernelTimer(sysprop::support_kernel_idle_timer(<span class="literal">false</span>)),</span><br><span class="line">        mPrimaryDispSync(createDispSync(mSupportKernelTimer)),    <span class="comment">//调用createDispSync函数</span></span><br><span class="line">        mEventControlThread(<span class="keyword">new</span> impl::EventControlThread(<span class="built_in">std</span>::move(function))),</span><br><span class="line">        mSchedulerCallback(schedulerCallback),</span><br><span class="line">        mRefreshRateConfigs(refreshRateConfig),</span><br><span class="line">        mUseContentDetection(useContentDetection),</span><br><span class="line">        mUseContentDetectionV2(useContentDetectionV2) &#123; ....</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;DispSync&gt; <span class="title">createDispSync</span><span class="params">(<span class="keyword">bool</span> supportKernelTimer)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">std</span>::make_unique&lt;impl::DispSync&gt;(<span class="string">"SchedulerDispSync"</span>,</span><br><span class="line">                                                sysprop::running_without_sync_framework(<span class="literal">true</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>surfaceflinger/Scheduler/DispSync.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">//构造函数</span></span><br><span class="line">DispSync::DispSync(<span class="keyword">const</span> <span class="keyword">char</span>* name, <span class="keyword">bool</span> hasSyncFramework)</span><br><span class="line">      : mName(name), mIgnorePresentFences(!hasSyncFramework) &#123;</span><br><span class="line">    mThread = <span class="keyword">new</span> DispSyncThread(name, mTraceDetailedInfo);   <span class="comment">//创建DispSyncThread对象</span></span><br><span class="line">    mThread-&gt;run(<span class="string">"DispSync"</span>, PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);   <span class="comment">//线程执行</span></span><br><span class="line">    ...</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DispSyncThread</span> :</span> <span class="keyword">public</span> Thread &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    DispSyncThread(<span class="keyword">const</span> <span class="keyword">char</span>* name, <span class="keyword">bool</span> showTraceDetailedInfo)</span><br><span class="line">          : mName(name),</span><br><span class="line">            mStop(<span class="literal">false</span>),</span><br><span class="line">            mModelLocked(<span class="string">"DispSync:ModelLocked"</span>, <span class="literal">false</span>),</span><br><span class="line">            mPeriod(<span class="number">0</span>),     <span class="comment">//初始化为0</span></span><br><span class="line">            mPhase(<span class="number">0</span>),</span><br><span class="line">            mReferenceTime(<span class="number">0</span>),</span><br><span class="line">            mWakeupLatency(<span class="number">0</span>),</span><br><span class="line">            mFrameNumber(<span class="number">0</span>),</span><br><span class="line">            mTraceDetailedInfo(showTraceDetailedInfo) &#123;&#125;</span><br><span class="line">            ......   </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="keyword">bool</span> <span class="title">threadLoop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">status_t</span> err;</span><br><span class="line">        <span class="keyword">nsecs_t</span> now = systemTime(SYSTEM_TIME_MONOTONIC);</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">//当threadLoop第一次进来后，由于mPeriod初始化为0，所以一直死等在这里</span></span><br><span class="line">                <span class="keyword">if</span> (mPeriod == <span class="number">0</span>) &#123;</span><br><span class="line">                    err = mCond.wait(mMutex);</span><br><span class="line">                    <span class="keyword">if</span> (err != NO_ERROR) &#123;</span><br><span class="line">                        ALOGE(<span class="string">"error waiting for new events: %s (%d)"</span>, strerror(-err), err);</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 class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//计算下一次vsync事件的时间</span></span><br><span class="line">                targetTime = computeNextEventTimeLocked(now);</span><br><span class="line">                .....</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><hr><h3 id="addEventListener唤醒线程DispSyncThread"><a href="#addEventListener唤醒线程DispSyncThread" class="headerlink" title="addEventListener唤醒线程DispSyncThread"></a>addEventListener唤醒线程DispSyncThread</h3><p>接着上面threadmain函数流程调用到addEventListener，此处将唤醒上面threadloop的<code>mCond.wait(mMutex)</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">addEventListener</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* name, <span class="keyword">nsecs_t</span> phase, DispSync::Callback* callback,</span></span></span><br><span class="line"><span class="function"><span class="params">                              <span class="keyword">nsecs_t</span> lastCallbackTime)</span> </span>&#123;</span><br><span class="line">            ...</span><br><span class="line">        mEventListeners.push_back(listener);</span><br><span class="line">        <span class="comment">//唤醒线程，threadLoop可以跳出循环继续执行</span></span><br><span class="line">        mCond.signal();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>而<code>mPeriod == 0)</code>则仍旧是0，所以此时会<code>continue</code>继续往下执行。计算下一个Vsync信号的时间戳，并且上报给EventListener，如此DispSyncThread模型就会运作起来。</p><hr><h3 id="HWComposer回调onVysncReceived"><a href="#HWComposer回调onVysncReceived" class="headerlink" title="HWComposer回调onVysncReceived"></a>HWComposer回调onVysncReceived</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">surfaceflinger&#x2F;DisplayHardware&#x2F;HWComposer.cpp  -- class ComposerCallbackBridge - android::hardware::Return&lt;void&gt; onVsync  ----&gt;</span><br><span class="line">SurfaceFlinger::onVsyncReceived   ----&gt;</span><br><span class="line">(1) addResyncSample 更新vysnc计算模型</span><br><span class="line">(2) VSyncModulator::onRefreshRateChangeCompleted</span><br></pre></td></tr></table></figure><p>此处会调用到addResyncSample函数，该函数会加入硬件vsync的样本，目的是为了计算、更新SW VSYNC的参数。具体解释将对应小节。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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">void</span> <span class="title">SurfaceFlinger::onVsyncReceived</span><span class="params">(<span class="keyword">int32_t</span> sequenceId, hal::HWDisplayId hwcDisplayId,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     <span class="keyword">int64_t</span> timestamp,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     <span class="built_in">std</span>::optional&lt;hal::VsyncPeriodNanos&gt; vsyncPeriod)</span> </span>&#123;</span><br><span class="line">    ATRACE_NAME(<span class="string">"SF onVsync"</span>);  <span class="comment">//systrace</span></span><br><span class="line"></span><br><span class="line">    <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mStateLock)</span></span>;</span><br><span class="line">    <span class="comment">// Ignore any vsyncs from a previous hardware composer.</span></span><br><span class="line">    <span class="keyword">if</span> (sequenceId != getBE().mComposerSequenceId) &#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">if</span> (!getHwComposer().onVsync(hwcDisplayId, timestamp)) &#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">bool</span> periodFlushed = <span class="literal">false</span>;</span><br><span class="line">    mScheduler-&gt;addResyncSample(timestamp, vsyncPeriod, &amp;periodFlushed);</span><br><span class="line">    <span class="keyword">if</span> (periodFlushed) &#123;</span><br><span class="line">        mVSyncModulator-&gt;onRefreshRateChangeCompleted();</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">//surfaceflinger/Scheduler/VSyncModulator.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">VSyncModulator::onRefreshRateChangeCompleted</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!mRefreshRateChangePending) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    mRefreshRateChangePending = <span class="literal">false</span>;</span><br><span class="line">    updateOffsets();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">VSyncModulator::updateOffsets</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line">    updateOffsetsLocked();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">VSyncModulator::updateOffsetsLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> Offsets&amp; offsets = getNextOffsets();</span><br><span class="line"></span><br><span class="line">    mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);</span><br><span class="line">    mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);</span><br><span class="line"></span><br><span class="line">    mOffsets = offsets;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mTraceDetailedInfo) &#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">const</span> <span class="keyword">bool</span> isEarly = &amp;offsets == &amp;mOffsetsConfig.early;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">bool</span> isEarlyGl = &amp;offsets == &amp;mOffsetsConfig.earlyGl;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">bool</span> isLate = &amp;offsets == &amp;mOffsetsConfig.late;</span><br><span class="line"></span><br><span class="line">    ATRACE_INT(<span class="string">"Vsync-EarlyOffsetsOn"</span>, isEarly);</span><br><span class="line">    ATRACE_INT(<span class="string">"Vsync-EarlyGLOffsetsOn"</span>, isEarlyGl);</span><br><span class="line">    ATRACE_INT(<span class="string">"Vsync-LateOffsetsOn"</span>, isLate);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="计算SW-vsync并向APP-SF发送vsync信号"><a href="#计算SW-vsync并向APP-SF发送vsync信号" class="headerlink" title="计算SW vsync并向APP/SF发送vsync信号"></a>计算SW vsync并向APP/SF发送vsync信号</h2><p>在计算完后，继续执行DispSyncThread的threadloop函数：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//surfaceflinger/Scheduler/DispSync.cpp</span></span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">bool</span> <span class="title">threadLoop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">status_t</span> err;</span><br><span class="line">        <span class="keyword">nsecs_t</span> now = systemTime(SYSTEM_TIME_MONOTONIC);</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">//计算下一次vsync事件的时间</span></span><br><span class="line">        targetTime = computeNextEventTimeLocked(now);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">bool</span> isWakeup = <span class="literal">false</span>;</span><br><span class="line">                <span class="comment">//等待计算出来的下一次vsync时间到来</span></span><br><span class="line">                <span class="comment">//如果到了就发送SW VSYNC信号</span></span><br><span class="line">                <span class="keyword">if</span> (now &lt; targetTime) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (mTraceDetailedInfo) ATRACE_NAME(<span class="string">"DispSync waiting"</span>);</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (targetTime == INT64_MAX) &#123;</span><br><span class="line">                        ALOGV(<span class="string">"[%s] Waiting forever"</span>, mName);</span><br><span class="line">                        err = mCond.wait(mMutex);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        ALOGV(<span class="string">"[%s] Waiting until %"</span> PRId64, mName, ns2us(targetTime));</span><br><span class="line">                        <span class="comment">//等到SW VSYNC时间到了，就唤醒，发送vsync信号</span></span><br><span class="line">                        err = mCond.waitRelative(mMutex, targetTime - now);</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (err == TIMED_OUT) &#123;</span><br><span class="line">                        isWakeup = <span class="literal">true</span>;</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (err != NO_ERROR) &#123;</span><br><span class="line">                        ALOGE(<span class="string">"error waiting for next event: %s (%d)"</span>, strerror(-err), err);</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">                &#125;</span><br><span class="line"></span><br><span class="line">                now = systemTime(SYSTEM_TIME_MONOTONIC);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//计算wake up时间, 但是不能超过1.5 ms</span></span><br><span class="line">                <span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">nsecs_t</span> kMaxWakeupLatency = us2ns(<span class="number">1500</span>);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (isWakeup) &#123;</span><br><span class="line">                    <span class="comment">//累加mWakeupLatency醒来的时间</span></span><br><span class="line">                    <span class="comment">//用于在后面计算SW VSYNC时间</span></span><br><span class="line">                    <span class="comment">//所有的wake up时间最大不能超过1.5 ms</span></span><br><span class="line">                    mWakeupLatency = ((mWakeupLatency * <span class="number">63</span>) + (now - targetTime)) / <span class="number">64</span>;</span><br><span class="line">                    mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);</span><br><span class="line">                    <span class="keyword">if</span> (mTraceDetailedInfo) &#123;</span><br><span class="line">                        ATRACE_INT64(<span class="string">"DispSync:WakeupLat"</span>, now - targetTime);</span><br><span class="line">                        ATRACE_INT64(<span class="string">"DispSync:AvgWakeupLat"</span>, mWakeupLatency);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//收回回调的EventListener（在前面addEventListener中）</span></span><br><span class="line">                <span class="comment">//</span></span><br><span class="line">                callbackInvocations =</span><br><span class="line">                        gatherCallbackInvocationsLocked(now, computeNextRefreshLocked(<span class="number">0</span>, now));</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (callbackInvocations.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">//向SF/APP EventThread发送vsync信号</span></span><br><span class="line">                fireCallbackInvocations(callbackInvocations);</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="literal">false</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="计算SW-Vsync下一个vsync时间"><a href="#计算SW-Vsync下一个vsync时间" class="headerlink" title="计算SW Vsync下一个vsync时间"></a>计算SW Vsync下一个vsync时间</h3><p>这里其实就最多只有两种EventListener, 一个是SF EventThread,一个是App EventThread。</p><p>它们都需要接收Vsync信号来分别做不同的事情。</p><p>但是实际上两个线程都有一个vsync phase offset偏移值</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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"><span class="comment">//surfaceflinger/Scheduler/DispSync.cpp</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="function"><span class="keyword">nsecs_t</span> <span class="title">computeNextEventTimeLocked</span><span class="params">(<span class="keyword">nsecs_t</span> now)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mTraceDetailedInfo) ATRACE_CALL();</span><br><span class="line">        ALOGV(<span class="string">"[%s] computeNextEventTimeLocked"</span>, mName);</span><br><span class="line">        <span class="keyword">nsecs_t</span> nextEventTime = INT64_MAX;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; mEventListeners.size(); i++) &#123;</span><br><span class="line">            <span class="comment">//对所有的EventListener进行分别计算，里面的mLastEventTime值不同</span></span><br><span class="line">            <span class="keyword">nsecs_t</span> t = computeListenerNextEventTimeLocked(mEventListeners[i], now);</span><br><span class="line">            <span class="keyword">if</span> (t &lt; nextEventTime) &#123;</span><br><span class="line">                nextEventTime = t;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nextEventTime;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">nsecs_t</span> <span class="title">computeListenerNextEventTimeLocked</span><span class="params">(<span class="keyword">const</span> EventListener&amp; listener, <span class="keyword">nsecs_t</span> baseTime)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mTraceDetailedInfo) ATRACE_CALL();</span><br><span class="line">        <span class="comment">//计算的是上一次vsync事件的时间，等于上一次vsync事件 + wake up唤醒时间</span></span><br><span class="line">        <span class="keyword">nsecs_t</span> lastEventTime = listener.mLastEventTime + mWakeupLatency;</span><br><span class="line">        <span class="comment">//一般情况是fasle;</span></span><br><span class="line">        <span class="comment">//如果是true，比如第一次，threadLoop的now生成的时间比较早，而addEventListener发生的比较晚,</span></span><br><span class="line">        <span class="comment">//listener的lastEventTime设为了当前的系统时间，此时baseTime就会小于lastEventTime</span></span><br><span class="line">        <span class="keyword">if</span> (baseTime &lt; lastEventTime) &#123;</span><br><span class="line">            <span class="comment">//重新修正baseTime</span></span><br><span class="line">            baseTime = lastEventTime;</span><br><span class="line">            ALOGV(<span class="string">"[%s] Clamping baseTime to lastEventTime -&gt; %"</span> PRId64, mName, ns2us(baseTime));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//减去参考时间（第一个硬件vsync样本的时间）</span></span><br><span class="line">        baseTime -= mReferenceTime;</span><br><span class="line">        <span class="comment">//mPhase是通过硬件vsync样本计算出来的</span></span><br><span class="line">        <span class="comment">//listener.mPhase是固定的具体在编译时设置的（在PhaseOffsets.cpp中）</span></span><br><span class="line">        <span class="keyword">nsecs_t</span> phase = mPhase + listener.mPhase;</span><br><span class="line">        <span class="comment">//减去偏移</span></span><br><span class="line">        baseTime -= phase;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (baseTime &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            ALOGV(<span class="string">"[%s] Correcting negative baseTime"</span>, mName);</span><br><span class="line">            baseTime = -mPeriod;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//求出下一时刻发送sw vsync的时间，这个时间是以第一个硬件vsync作为参考来这样计算</span></span><br><span class="line">        <span class="comment">//如果以一个sw vsync时间作为参考，因为sw vsync的时间本身就是一种根据模型模拟出来的，所以本身存在误差，</span></span><br><span class="line">        <span class="comment">//所以如果每个sw vsync以上一个作为base的话,那么它的误差会慢慢积累;</span></span><br><span class="line">        <span class="comment">//而每次以第一个硬件vsync时间作为基准，那么每个sw vsync的误差，并不会累加，这样就相对来说更加精确些</span></span><br><span class="line">        <span class="keyword">nsecs_t</span> numPeriods = baseTime / mPeriod;</span><br><span class="line">        <span class="comment">//计算距离第一个硬件Vsync时间的偏移，即得到下一个sw vsync的时间，numPeriods + 1,注意是下一个vsync的时间</span></span><br><span class="line">        <span class="keyword">nsecs_t</span> t = (numPeriods + <span class="number">1</span>) * mPeriod + phase;</span><br><span class="line">        <span class="comment">//t是相对于每一个硬件vsync的时间</span></span><br><span class="line">        t += mReferenceTime;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果这个vsync距离上一个vsync时间小于3/5个mPeriod的话，为了避免连续的两个sw vsync,</span></span><br><span class="line">        <span class="comment">//那么这次sw vsync就放弃了，直接放到下一个周期里</span></span><br><span class="line">        <span class="keyword">if</span> (isCloseToPeriod(t - listener.mLastEventTime)) &#123;</span><br><span class="line">            t += mPeriod;</span><br><span class="line">            ALOGV(<span class="string">"[%s] Modifying t -&gt; %"</span> PRId64, mName, ns2us(t));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//当然算出来的时间要减去wake up的时间了，这样才能精确的模拟硬件vsync的时间，</span></span><br><span class="line">        <span class="comment">//注意mWakeupLatency是所有wake up的时间累加，但是最大只能到1.5ms</span></span><br><span class="line">        t -= mWakeupLatency;</span><br><span class="line">        ALOGV(<span class="string">"[%s] Corrected for wakeup latency -&gt; %"</span> PRId64, mName, ns2us(t));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">isCloseToPeriod</span><span class="params">(<span class="keyword">nsecs_t</span> duration)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Ratio of 3/5 is arbitrary, but it must be greater than 1/2.</span></span><br><span class="line">        <span class="keyword">return</span> duration &lt; (<span class="number">3</span> * mPeriod) / <span class="number">5</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="向SF-APP-EventThread发送vsync信号"><a href="#向SF-APP-EventThread发送vsync信号" class="headerlink" title="向SF/APP EventThread发送vsync信号"></a>向SF/APP EventThread发送vsync信号</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//surfaceflinger/Scheduler/DispSync.cpp</span></span><br><span class="line">    <span class="function"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;CallbackInvocation&gt; <span class="title">gatherCallbackInvocationsLocked</span><span class="params">(<span class="keyword">nsecs_t</span> now,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                                    <span class="keyword">nsecs_t</span> expectedVSyncTime)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mTraceDetailedInfo) ATRACE_CALL();</span><br><span class="line"></span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;CallbackInvocation&gt; callbackInvocations;</span><br><span class="line">        <span class="comment">//因为computeListenerNextEventTimeLocked计算的是下一个vsync时间，</span></span><br><span class="line">        <span class="comment">//那么这一次的vsync就以上now - mPeriod作为基准时间</span></span><br><span class="line">        <span class="keyword">nsecs_t</span> onePeriodAgo = now - mPeriod;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; eventListener : mEventListeners) &#123;</span><br><span class="line">            <span class="keyword">nsecs_t</span> t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (t &lt; now) &#123;</span><br><span class="line">                <span class="keyword">if</span> (isCloseToPeriod(now - eventListener.mLastCallbackTime)) &#123;</span><br><span class="line">                    eventListener.mLastEventTime = t;</span><br><span class="line">                    ALOGV(<span class="string">"[%s] [%s] Skipping event due to model error"</span>, mName,</span><br><span class="line">                          eventListener.mName);</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">                CallbackInvocation ci;</span><br><span class="line">                ci.mCallback = eventListener.mCallback;</span><br><span class="line">                ci.mEventTime = t;</span><br><span class="line">                ci.mExpectedVSyncTime = expectedVSyncTime;</span><br><span class="line">                <span class="keyword">if</span> (eventListener.mPhase &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                    ci.mExpectedVSyncTime += mPeriod;</span><br><span class="line">                &#125;</span><br><span class="line">                ALOGV(<span class="string">"[%s] [%s] Preparing to fire, latency: %"</span> PRId64, mName, eventListener.mName,</span><br><span class="line">                      t - eventListener.mLastEventTime);</span><br><span class="line">                <span class="comment">//回调</span></span><br><span class="line">                callbackInvocations.push_back(ci);</span><br><span class="line">                eventListener.mLastEventTime = t;</span><br><span class="line">                eventListener.mLastCallbackTime = now;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> callbackInvocations;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><hr><h2 id="postComposition更新SW-Vsync的误差值"><a href="#postComposition更新SW-Vsync的误差值" class="headerlink" title="postComposition更新SW Vsync的误差值"></a>postComposition更新SW Vsync的误差值</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//surfaceflinger/SurfaceFlinger.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::postComposition</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"postComposition"</span>);</span><br><span class="line">    ...</span><br><span class="line">    getBE().mDisplayTimeline.updateSignalTimes();</span><br><span class="line">    mPreviousPresentFences[<span class="number">1</span>] = mPreviousPresentFences[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">    sp&lt;DisplayDevice&gt; vSyncSource = mNextVsyncSource;</span><br><span class="line">    <span class="keyword">if</span> (mNextVsyncSource == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        vSyncSource = mActiveVsyncSource;</span><br><span class="line">    &#125;</span><br><span class="line">    mPreviousPresentFences[<span class="number">0</span>] = vSyncSource ?</span><br><span class="line">        getHwComposer().getPresentFence(*vSyncSource-&gt;getId()) : Fence::NO_FENCE;</span><br><span class="line">    <span class="keyword">auto</span> presentFenceTime = <span class="built_in">std</span>::make_shared&lt;FenceTime&gt;(mPreviousPresentFences[<span class="number">0</span>]);</span><br><span class="line">    getBE().mDisplayTimeline.push(presentFenceTime);</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//获取fence</span></span><br><span class="line">    mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[<span class="number">0</span>]);</span><br><span class="line">    mTransactionCompletedThread.sendCallbacks();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (display &amp;&amp; display-&gt;isPrimary() &amp;&amp; display-&gt;getPowerMode() == hal::PowerMode::ON &amp;&amp;</span><br><span class="line">        presentFenceTime-&gt;isValid()) &#123;</span><br><span class="line">        mScheduler-&gt;addPresentFence(presentFenceTime);</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="comment">//surfaceflinger/Scheduler/Scheduler.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Scheduler::addPresentFence</span><span class="params">(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">shared_ptr</span>&lt;FenceTime&gt;&amp; fenceTime)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mPrimaryDispSync-&gt;addPresentFence(fenceTime)) &#123;</span><br><span class="line">        <span class="comment">//如果addPresentFence返回true，则说明SW vsync和硬件Vsync的误差太大</span></span><br><span class="line">        <span class="comment">//此时就需要重新打开硬件vsync，来重新调节计算SW vsync模型</span></span><br><span class="line">        enableHardwareVsync();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        disableHardwareVsync(<span class="literal">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="addPresentFence"><a href="#addPresentFence" class="headerlink" title="addPresentFence"></a>addPresentFence</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//surfaceflinger/Scheduler/DispSync.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">DispSync::addPresentFence</span><span class="params">(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">shared_ptr</span>&lt;FenceTime&gt;&amp; fenceTime)</span> </span>&#123;</span><br><span class="line">    <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mIgnorePresentFences) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//将当前硬件vsync的fence保存到mPresentFences中，</span></span><br><span class="line">    <span class="comment">//目的是为了计算偏移</span></span><br><span class="line">    mPresentFences[mPresentSampleOffset] = fenceTime;</span><br><span class="line">    mPresentSampleOffset = (mPresentSampleOffset + <span class="number">1</span>) % NUM_PRESENT_SAMPLES;</span><br><span class="line">    <span class="comment">//置为0</span></span><br><span class="line">    mNumResyncSamplesSincePresent = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//更新错误信息</span></span><br><span class="line">    updateErrorLocked();</span><br><span class="line">    <span class="comment">//一般情况下mModelUpdated已经被更新，然后硬件vsync被disable</span></span><br><span class="line">    <span class="comment">//所以这里只需要看SW VSYNC的真实的硬件vsync的误差是否在可允许的范围内即可</span></span><br><span class="line">    <span class="keyword">return</span> !mModelUpdated || mError &gt; kErrorThreshold;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的mError是方差，当方差大于kErrorThreshold就返回true。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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"><span class="comment">//surfaceflinger/Scheduler/DispSync.cpp</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DispSync::updateErrorLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!mModelUpdated) &#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">int</span> numErrSamples = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">nsecs_t</span> sqErrSum = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//NUM_PRESENT_SAMPLES=8</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; NUM_PRESENT_SAMPLES; i++) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">nsecs_t</span> time = mPresentFences[i]-&gt;getCachedSignalTime();</span><br><span class="line">        <span class="keyword">if</span> (time == Fence::SIGNAL_TIME_PENDING || time == Fence::SIGNAL_TIME_INVALID) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//mReferenceTime是第一个硬件vsync的时间戳（addResyncSample中）</span></span><br><span class="line">        <span class="keyword">nsecs_t</span> sample = time - mReferenceTime;</span><br><span class="line">        <span class="comment">//此处sample一般情况下是大于偏移量mPhase的</span></span><br><span class="line">        <span class="keyword">if</span> (sample &lt;= mPhase) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="number">5</span> </span><br><span class="line">        <span class="keyword">nsecs_t</span> sampleErr = (sample - mPhase) % mPeriod;</span><br><span class="line">        <span class="keyword">if</span> (sampleErr &gt; mPeriod / <span class="number">2</span>) &#123;</span><br><span class="line">            sampleErr -= mPeriod;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//记录偏移差的平方和</span></span><br><span class="line">        sqErrSum += sampleErr * sampleErr;</span><br><span class="line">        numErrSamples++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//mError即求出的方差</span></span><br><span class="line">    <span class="keyword">if</span> (numErrSamples &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        mError = sqErrSum / numErrSamples;</span><br><span class="line">        mZeroErrSamplesCount = <span class="number">0</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        mError = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam.</span></span><br><span class="line">        mZeroErrSamplesCount++;</span><br><span class="line">        ALOGE_IF((mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == <span class="number">0</span>,</span><br><span class="line">                 <span class="string">"No present times for model error."</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mTraceDetailedInfo) &#123;</span><br><span class="line">        ATRACE_INT64(<span class="string">"DispSync:Error"</span>, mError);</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><blockquote><p>参考google文章： <a href="ttps://zhuanlan.zhihu.com/p/142212769?from_voters_page=true">在 Android 上进行高刷新率渲染</a></p><p>Google官网<a href="https://source.android.google.cn/devices/graphics/multiple-refresh-rate" target="_blank" rel="noopener">Multiple Refresh Rate</a></p><p>参考： <a href="https://blog.csdn.net/u014535072/article/month/2020/05" target="_blank" rel="noopener">CCSDN博客 刷新率和Vsync</a></p><p>参考： <a href="https://www.jianshu.com/p/d3e4b1805c92" target="_blank" rel="noopener">Android SurfaceFlinger SW Vsync模型</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;为了理解systrace中HW Vsync， sf vsync， app vsync的含义和作用。这里主要参照Android R AOSP源码对这几种VSYNC的关系和调用流程进行大致的梳理。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android Systrace的部分Tag含义</title>
    <link href="https://alonealive.github.io/Blog/2021/03/09/2021/210309_android_systraceTAG/"/>
    <id>https://alonealive.github.io/Blog/2021/03/09/2021/210309_android_systraceTAG/</id>
    <published>2021-03-09T13:52:00.000Z</published>
    <updated>2021-03-09T13:02:57.632Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>systrace的一些tag标签的含义和作用。</p></blockquote><a id="more"></a><h2 id="CPU-（0-7）"><a href="#CPU-（0-7）" class="headerlink" title="CPU*（0-7）"></a>CPU*（0-7）</h2><p>Kernel内核模块，可以查看各个CPU执行了什么进程任务。</p><p>cpu信息的目录是<code>/sys/devices/system/cpu</code>，例如我的一加六老设备：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">OnePlus6:&#x2F;sys&#x2F;devices&#x2F;system&#x2F;cpu $ ls</span><br><span class="line">core_ctl_isolated cpu4    cpuidle               isolated   possible </span><br><span class="line">cpu0              cpu5    gladiator_hang_detect kernel_max power    </span><br><span class="line">cpu1              cpu6    hang_detect_gold      modalias   present  </span><br><span class="line">cpu2              cpu7    hang_detect_silver    offline    uevent   </span><br><span class="line">cpu3              cpufreq hotplug               online</span><br></pre></td></tr></table></figure><hr><h2 id="HW-VSYNC-ON-XXX"><a href="#HW-VSYNC-ON-XXX" class="headerlink" title="HW_VSYNC_ON_XXX"></a>HW_VSYNC_ON_XXX</h2><ul><li>值：1表示HW VSYNC信号被打开，0关闭</li><li>出现时间：HW VSYNC硬件信号被打开/关闭的时候</li><li>含义：</li></ul><ol><li>HW_VSYNC_ON_XXX后面的XXX表示display id（可通过dump SurfaceFlinger查看），用于区分不同屏幕的HW VSYNC硬件信号</li><li>HW VSYNC硬件信号之所以会有时候被打开/关闭，是因为目前Android Graphics/Display依赖的信号不是硬件信号，而是软件信号 —— DispSync。<code>硬件信号的主要作用是用于校准，打开的时机是当软件VSYNC的误差超过一定值后，DispSync会打开HW VSYNC硬件信号进行校准</code>。</li></ol><ul><li>作用：用作分析硬件模块导致的BUG的时间点</li></ul><hr><h3 id="fence同步机制简单释义"><a href="#fence同步机制简单释义" class="headerlink" title="fence同步机制简单释义"></a>fence同步机制简单释义</h3><blockquote><p>BufferQueue中的Buffer在整个绘制、合成、显示的过程中，一直在 CPU，GPU 和 HWC 之前传递，某一方要使用 Buffer 之前，需要检查之前的使用者是否已经移交了 Buffer 的“使用权”。而这里的“使用权”，就是fence。当fence释放（即signal）的时候，说明 Buffer 的上一个使用者已经交出了使用权，对于 Buffer 进行操作是安全的。</p></blockquote><p>在 Android 里面，总共有三类fence：<code>acquire fence，release fence 和 present fence</code>。其中acquire fence和release fence隶属于Layer，present fence隶属于帧（即 Layers）：</p><ul><li><code>acquire fence</code>：App将Buffer通过queueBuffer()还给BufferQueue的时候，此时该Buffer的GPU侧其实是还没有完成的，此时会带上一个fence，这个fence就是acquire fence。当SurfaceFlinger/HWC要读取Buffer以进行合成操作的时候，需要等acquire fence释放之后才行</li><li><code>release fence</code>：当App通过dequeueBuffer()从BufferQueue申请Buffer，要对Buffer进行绘制的时候，需要保证HWC已经不再需要这个Buffer了，即需要等release fence signal才能对 Buffer进行写操作。</li><li><code>present fence</code>：在HWC1的时候称为retire fence，在HWC2中改名为present fence。当前帧成功显示到屏幕的时候，<code>present fence就会signal</code>。</li></ul><hr><h2 id="HW-VSYNC-XXX"><a href="#HW-VSYNC-XXX" class="headerlink" title="HW_VSYNC_XXX"></a>HW_VSYNC_XXX</h2><ul><li>值：值发生变化（0-&gt;1 / 1-&gt;0）是表示当前时刻发出了HW VSYNC硬件信号</li><li>出现时间：上面HW_VSYNC_ON_XXX打开的时候</li><li>含义：</li></ul><ol><li>HW_VSYNC_XXX记录的是当前时刻收到了HW VYSNC硬件信号</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp</span></span><br><span class="line"><span class="comment">//Step 1: 当收到硬件信号后，回调该函数</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">HWComposer::onVsync</span><span class="params">(hal::HWDisplayId hwcDisplayId, <span class="keyword">int64_t</span> timestamp)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span> displayId = toPhysicalDisplayId(hwcDisplayId);</span><br><span class="line">    ......</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span> tag = <span class="string">"HW_VSYNC_"</span> + to_string(*displayId);</span><br><span class="line">    ATRACE_INT(tag.c_str(), displayData.vsyncTraceToggle);</span><br><span class="line">    <span class="comment">//Step 2: 取反赋值给HW_VSYNC_XXX，即systrace中该值0和1之间的变化</span></span><br><span class="line">    displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="hasClientComposition"><a href="#hasClientComposition" class="headerlink" title="hasClientComposition"></a>hasClientComposition</h2><ul><li>值：布尔值，1表示本次SurfaceFlinger合成存在GPU合成的layer，0表示本次合成的所有layer都不是GPU合成</li><li>出现时间：SurfaceFlinger的doComposition合成开始</li><li>含义：</li></ul><ol><li>Client合成（GPU合成）是通过CompositionEngine调用一系列的OpenGLES接口完成合成</li><li>Device合成（硬件合成）是使用Hardware Composer进行合成。</li></ol><p><strong>优势</strong>：省电</p><p><strong>劣势</strong>：某些场景无法进行Device合成，比如Layer是圆角的，Layer的总数超过硬件上限</p><ul><li>作用：如果发现本应该是Device合成的场景出现hasClientComposition值为1的情况，则可结合dump SurfaceFlinger信息分析合成策略是否有问题</li></ul><hr><h2 id="FrameMissed-GpuFrameMissed-HwcFrameMissed"><a href="#FrameMissed-GpuFrameMissed-HwcFrameMissed" class="headerlink" title="FrameMissed/GpuFrameMissed/HwcFrameMissed"></a>FrameMissed/GpuFrameMissed/HwcFrameMissed</h2><ul><li><p>值：1表示<code>上一次合成</code>有FrameMissed/GpuFrameMissed/HwcFrameMissed，0则没有</p></li><li><p>出现时间：SurfaceFlinger的INVALIDATE阶段开始</p></li><li><p>含义：<code>FrameMissed/GpuFrameMissed/HwcFrameMissed表示的是上一次合成的结果，当SurfaceFlinger合成后显示到屏幕上显示一帧，present fence就会signal。因此可以将present fence signal作为一次合成完结的标志。</code>SurfaceFlinger每次开始被Vysnc-sf唤醒时，会先检查上一次合成情况，方式就是检查上一次合成的present fence有没有signal。如果没有，则认为是FrameMissed，并结合上一次合成方式是否有GPU或者HWC参与，同步GpuFrameMissed/HwcFrameMissed信息。</p></li><li><p>present fence没有及时signal主要有两种原因：</p></li></ul><ol><li>Display问题</li><li>APP/游戏的GPU负载过高：上层GPU负载过高会导致底层大部分时间都在等GPU渲染工作完成，延迟了present fence的signal，导致FrameMissed</li></ol><ul><li>FrameMissed作用：</li></ul><ol><li>统计丢帧</li><li>Android有一个debug开关，可以在检测到上一帧有FrameMissed出现的时候，跳过本次的合成，留给底层更多的时间去显示。这个初衷是好的，不让底层过于繁忙，通过主动跳过合成来减缓底层的工作量。但是由于跳过合成就相当于主动丢帧，在某些场景下会导致到持续性的掉帧。因此这个开关一般是不会打开的。</li></ol><hr><h2 id="VSYNC-sf-VSYNC-app"><a href="#VSYNC-sf-VSYNC-app" class="headerlink" title="VSYNC-sf/VSYNC-app"></a>VSYNC-sf/VSYNC-app</h2><ul><li>值：表示SurfaceFlinger和APP发出Vsync信号（刷新率60是16.67ms，刷新率90是11.11ms）</li><li>出现时间：DispSync分发的时候</li><li>作用：分别针对SurfaceFlinger合成和APP应用渲染的起点。<code>如果一处没有Vsync-sf，则说明此处有丢帧情况，原因可能是上面的FrameMissed，或者是APP没有及时完成渲染导致丢帧</code></li></ul><h3 id="VSYNC-app基本不变化的原因"><a href="#VSYNC-app基本不变化的原因" class="headerlink" title="VSYNC-app基本不变化的原因"></a>VSYNC-app基本不变化的原因</h3><p>现在绝大部分手游都使用游戏引擎，例如Unity、Unreal，这些引擎会自己去控制刷新率。</p><p>例如和平精英、王者荣耀可以有多个帧率档位选择，所以就不会通过Vsync-app的速率进行绘制刷新。</p><hr><h2 id="queueBuffer"><a href="#queueBuffer" class="headerlink" title="queueBuffer"></a>queueBuffer</h2><p>Systrace中抓取的都是CPU侧的，例如每个CPU核的频率、C-State、运行了什么线程、线程间的调用关系、运行时长等。</p><p>而生产者通过dequeuebuffer获取buffer后，会最终交给GPU渲染绘制，然后通过queuebuffer将buffer还给BufferQueue。</p><p><code>如果GPU渲染的时间长，则可以初步判断性能问题是出自于GPU侧</code></p><h3 id="FenceMonitor"><a href="#FenceMonitor" class="headerlink" title="FenceMonitor"></a>FenceMonitor</h3><p>Android Q在libgui库引入新的内部类FenceMonitor，作用是跟踪Fence的生命周期，在Systrace中展示一个Fence从产生到signal需要的时间。</p><ol><li><strong>在Systrace中GPU Completion的每个<code>waiting for GPU completion ×××</code>的长度，大致可以作为GPU渲染所花费的时间（即acquire fence释放的总时间），但是并不严谨（解释如下）。</strong></li></ol><p>通过这个时间，可以判断是否有GPU bound的现象。</p><ol start="2"><li><strong>相对应的，<code>waiting for HWC release ×××</code>的长度大致可以作为release fence的释放总时间参考。在release fence signal之前，GPU是无法对dequeuebuffer拿到的Buffer进行读写的（因为此时Buffer还是归HWC所有）。</strong></li></ol><p>通过这点，可以判断Display是否有问题。</p><p><strong>注</strong>：systrace上的fence信息只能准确反映出signal的时间点，但无法反应出gpu/display开始干活的时间点。</p><p>再说原因：systrace上等待GPU fence的起始时间是从queueBuffer开始算的，而不是gpu真正开始干活时才算的。queueBuffer执行完成并不意味着gpu就能马上开始干活，有可能这个时候是因为display还没有释放（即signal）该buffer导致gpu不得不等在那里。所以我们不能说等待gpu的fence耗时就是gpu渲染的时间。同样的，HWC release fence也是一样的道理。</p><p>但有时候我们可以变相的计算出gpu的耗时：GPU complete signal的时间点，减去上一帧HWC release fence signal的时间点，这样计算出来的结果也只能是个近似值，因为从display buffer释放到gpu真正开始干活，这中间还有额外的准备时间。当然，有的时候systrace上反应不出HWC release fence signal的时间点，因为在dequeueBuffer的时候该release fence就已经释放了，所以上面的公式就排不上用场了。</p><hr><p>FenceMonitor相关代码：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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">//frameworks/native/libs/gui/Surface.cpp</span></span><br><span class="line"><span class="comment">//内部类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FenceMonitor</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    explicit FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) &#123;</span><br><span class="line">        <span class="function"><span class="built_in">std</span>::thread <span class="title">thread</span><span class="params">(&amp;FenceMonitor::loop, <span class="keyword">this</span>)</span></span>;</span><br><span class="line">        pthread_setname_np(thread.native_handle(), mName);</span><br><span class="line">        thread.detach();</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">int</span> <span class="title">Surface::dequeueBuffer</span><span class="params">(<span class="keyword">android_native_buffer_t</span>** buffer, <span class="keyword">int</span>* fenceFd)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"Surface::dequeueBuffer"</span>);</span><br><span class="line">    .....</span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) &#123;</span><br><span class="line">        <span class="comment">//申请到Buffer</span></span><br><span class="line">        <span class="function"><span class="keyword">static</span> FenceMonitor <span class="title">hwcReleaseThread</span><span class="params">(<span class="string">"HWC release"</span>)</span></span>;</span><br><span class="line">        hwcReleaseThread.queueFence(fence);</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">int</span> <span class="title">Surface::queueBuffer</span><span class="params">(<span class="keyword">android_native_buffer_t</span>* buffer, <span class="keyword">int</span> fenceFd)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"Surface::queueBuffer"</span>);</span><br><span class="line">    .....</span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) &#123;</span><br><span class="line">        <span class="comment">//GPU绘制完成</span></span><br><span class="line">        <span class="function"><span class="keyword">static</span> FenceMonitor <span class="title">gpuCompletionThread</span><span class="params">(<span class="string">"GPU completion"</span>)</span></span>;</span><br><span class="line">        gpuCompletionThread.queueFence(fence);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> err;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://wizzie.top/Blog/2020/02/22/2020/200222_android_systrace_study/" target="_blank" rel="noopener">Android Systrace如何抓取分析问题</a></li><li><a href="https://mp.weixin.qq.com/s/xgnXjhjPpJo27bbcDPW5RQ" target="_blank" rel="noopener">Systrace 中的这些 tag 究竟是什么意思（一）</a></li><li><a href="https://mp.weixin.qq.com/s/dFAVVXUu1FkY7taZbNKCjQ" target="_blank" rel="noopener">如何通过 Systrace 查看 GPU 渲染花费的时间</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;systrace的一些tag标签的含义和作用。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android Q SurfaceFlinger合成（二）</title>
    <link href="https://alonealive.github.io/Blog/2020/10/31/2020/201031_android_SurfaceFlinger2/"/>
    <id>https://alonealive.github.io/Blog/2020/10/31/2020/201031_android_SurfaceFlinger2/</id>
    <published>2020-10-31T10:42:00.000Z</published>
    <updated>2020-11-04T13:45:14.666Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>继上篇《Android　Ｑ SurfaceFlinger合成（一）》中SF对INVALIDATE信息处理，针对Layer属性变化、显示设备变化等情况处理，将mCurrentState提交到mDrawingState。然后遍历mDrawingState的Layer，将新的Buffer内容更新绑定到Layer纹理对象。经过这些流程，决定是否需要SF进行合成刷新，如果需要则调用<code>handleMessageRefresh</code>开始合成处理。</p></blockquote><a id="more"></a><h2 id="signalRefresh"><a href="#signalRefresh" class="headerlink" title="signalRefresh"></a>signalRefresh</h2><p>在onMessageReceivedINVALIDATE信息处理完成后，如果需要刷新，则会触发刷新：</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">SurfaceFlinger::onMessageReceived</span><span class="params">(<span class="keyword">int32_t</span> what)</span> NO_THREAD_SAFETY_ANALYSIS </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    <span class="keyword">switch</span> (what) &#123;</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::INVALIDATE: &#123;</span><br><span class="line">            ......</span><br><span class="line">            refreshNeeded |= mRepaintEverything;</span><br><span class="line">            <span class="comment">//在BootStage:：BOOTLOADER中时不要调用signalRefresh，不想用一个空白屏幕代替bootloader引导加载程序启动。</span></span><br><span class="line">            <span class="comment">//这样可以节省HWC不必要的工作</span></span><br><span class="line">            <span class="keyword">if</span> (refreshNeeded &amp;&amp; CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) &#123;</span><br><span class="line">                <span class="comment">//如果事务修改了窗口状态，新的Buffer被获取到，或者HWC已经请求一个新的repaint，则发出刷新信号</span></span><br><span class="line">                signalRefresh();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::REFRESH: &#123;</span><br><span class="line">            handleMessageRefresh();</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><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::signalRefresh</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mRefreshPending = <span class="literal">true</span>;</span><br><span class="line">    mEventQueue.refresh();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>需要刷新的情况：</strong></p><ol><li>有新的Transaction处理</li><li>PageFlip时，有Buffer更新</li><li>有重新合成请求时mRepaintEverything，这是响应HWC的请求时触发的。</li></ol><h3 id="MessageQueue分发refresh"><a href="#MessageQueue分发refresh" class="headerlink" title="MessageQueue分发refresh"></a>MessageQueue分发refresh</h3><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/MessageQueue.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">MessageQueue::refresh</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mHandler-&gt;dispatchRefresh();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> MessageQueue::Handler::dispatchRefresh() &#123;</span><br><span class="line">    <span class="keyword">if</span> ((android_atomic_or(eventMaskRefresh, &amp;mEventMask) &amp; eventMaskRefresh) == <span class="number">0</span>) &#123;</span><br><span class="line">        mQueue.mLooper-&gt;sendMessage(<span class="keyword">this</span>, Message(MessageQueue::REFRESH));</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">//最终回调handleMessage，处理REFRESH的message</span></span><br><span class="line"><span class="comment">//这过程中不用去等Vsync的，INVALIDATE时，是需要等Vsync的</span></span><br><span class="line"><span class="comment">//即INVALIDATE和REFRESH是在同一个Vsync周期内完成的</span></span><br><span class="line"><span class="keyword">void</span> MessageQueue::Handler::handleMessage(<span class="keyword">const</span> Message&amp; message) &#123;</span><br><span class="line">    <span class="keyword">switch</span> (message.what) &#123;</span><br><span class="line">        <span class="keyword">case</span> INVALIDATE:</span><br><span class="line">            android_atomic_and(~eventMaskInvalidate, &amp;mEventMask);</span><br><span class="line">            mQueue.mFlinger-&gt;onMessageReceived(message.what);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> REFRESH:</span><br><span class="line">            android_atomic_and(~eventMaskRefresh, &amp;mEventMask);</span><br><span class="line">            mQueue.mFlinger-&gt;onMessageReceived(message.what);</span><br><span class="line">            <span class="keyword">break</span>;</span><br></pre></td></tr></table></figure><h2 id="handleMessageRefresh刷新总流程"><a href="#handleMessageRefresh刷新总流程" class="headerlink" title="handleMessageRefresh刷新总流程"></a>handleMessageRefresh刷新总流程</h2><p>handleMessageRefresh函数包含了刷新（合成）一帧显示数据的所有流程。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::onMessageReceived</span><span class="params">(<span class="keyword">int32_t</span> what)</span> NO_THREAD_SAFETY_ANALYSIS </span>&#123;</span><br><span class="line">    ATRACE_CALL();d</span><br><span class="line">    <span class="keyword">switch</span> (what) &#123;</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::INVALIDATE: &#123;</span><br><span class="line">            ......</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::REFRESH: &#123;</span><br><span class="line">            handleMessageRefresh();</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><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::handleMessageRefresh</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line"></span><br><span class="line">    mRefreshPending = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">bool</span> repaintEverything = mRepaintEverything.exchange(<span class="literal">false</span>);</span><br><span class="line">    <span class="comment">//合成前预处理工作</span></span><br><span class="line">    preComposition();</span><br><span class="line">    <span class="comment">//计算和存储每个Layer的脏区域</span></span><br><span class="line">    rebuildLayerStacks();</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    calculateWorkingSet();<span class="comment">//和P不同</span></span><br><span class="line">    <span class="comment">//遍历Display</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; [token, display] : mDisplays) &#123;</span><br><span class="line">        beginFrame(display);<span class="comment">//和P不同</span></span><br><span class="line">        prepareFrame(display); <span class="comment">//和P不同</span></span><br><span class="line">        doDebugFlashRegions(display, repaintEverything);</span><br><span class="line">        <span class="comment">//先进行GL合成，将合成后的的图像放在HWC任务列表的最后为止</span></span><br><span class="line">        <span class="comment">//然后由HWC进行合成并输出到屏幕</span></span><br><span class="line">        doComposition(display, repaintEverything);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    logLayerStats();</span><br><span class="line"></span><br><span class="line">    postFrame();</span><br><span class="line">    <span class="comment">//合成善后工作</span></span><br><span class="line">    postComposition();</span><br><span class="line"></span><br><span class="line">    mHadClientComposition = <span class="literal">false</span>;</span><br><span class="line">    mHadDeviceComposition = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; [token, displayDevice] : mDisplays) &#123;</span><br><span class="line">        <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">auto</span> displayId = display-&gt;getId();</span><br><span class="line">        mHadClientComposition =</span><br><span class="line">                mHadClientComposition || getHwComposer().hasClientComposition(displayId);</span><br><span class="line">        mHadDeviceComposition =</span><br><span class="line">                mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mVsyncModulator.onRefreshed(mHadClientComposition);</span><br><span class="line">    mLayersWithQueuedFrames.clear();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="preComposition合成前预处理"><a href="#preComposition合成前预处理" class="headerlink" title="preComposition合成前预处理"></a>preComposition合成前预处理</h3><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">void</span> <span class="title">SurfaceFlinger::preComposition</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"preComposition"</span>);</span><br><span class="line"></span><br><span class="line">    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> needExtraInvalidate = <span class="literal">false</span>;</span><br><span class="line">    <span class="comment">//遍历所有需要进行合成的Layer（mDrawingState）</span></span><br><span class="line">    mDrawingState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">        <span class="comment">//调用onPreComposition，但绘制</span></span><br><span class="line">        <span class="keyword">if</span> (layer-&gt;onPreComposition(mRefreshStartTime)) &#123;</span><br><span class="line">            needExtraInvalidate = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">//通过上述函数的返回值来判断是否需要再次触发SurfaceFlinger接受Vsync合成</span></span><br><span class="line">    <span class="keyword">if</span> (needExtraInvalidate) &#123;</span><br><span class="line">        signalLayerUpdate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中<code>onPreComposition</code>函数的返回值针对不同Layer：</p><ul><li>ColorLayer和ContainLayer固定返回false</li><li>BufferLayer如下：</li></ul><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/BufferLayer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">bool</span> <span class="title">BufferLayer::onPreComposition</span><span class="params">(<span class="keyword">nsecs_t</span> refreshStartTime)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mBufferLatched) &#123;</span><br><span class="line">        <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mFrameEventHistoryMutex)</span></span>;</span><br><span class="line">        <span class="comment">//mFrameEventHistory记录PreComposition事件</span></span><br><span class="line">        mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);</span><br><span class="line">    &#125;</span><br><span class="line">    mRefreshPending = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">return</span> hasReadyFrame();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">BufferLayer::hasReadyFrame</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//****** BufferQueueLayer.cpp **********</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">BufferQueueLayer::hasFrameUpdate</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="comment">//之前在acquireBuffer的时候已经做了-1操作，而此处是现在BufferQueue中还有Buffer</span></span><br><span class="line">    <span class="comment">//（即仍有待处理的Buffer，就需要下次Vsync到来的时候再次执行合成）</span></span><br><span class="line">    <span class="keyword">return</span> mQueuedFrames &gt; <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">bool</span> <span class="title">BufferQueueLayer::getSidebandStreamChanged</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="comment">//SidebandStream改变</span></span><br><span class="line">    <span class="keyword">return</span> mSidebandStreamChanged;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">BufferQueueLayer::getAutoRefresh</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="comment">//自动刷新模式</span></span><br><span class="line">    <span class="keyword">return</span> mAutoRefresh;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="rebuildLayerStacks重构Layer栈"><a href="#rebuildLayerStacks重构Layer栈" class="headerlink" title="rebuildLayerStacks重构Layer栈"></a>rebuildLayerStacks重构Layer栈</h3><p>执行该函数，将完成创建Layer栈。</p><p>此时需要进行合成显示的数据已经被更新到每个Display各自的<code>layersSortedByZ</code>中。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::invalidateHwcGeometry</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    mGeometryInvalid = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::rebuildLayerStacks</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"rebuildLayerStacks"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// rebuild the visible layer list per screen</span></span><br><span class="line">    <span class="comment">// 前提是存在脏区域，即mVisibleRegionsDirty为true</span></span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(mVisibleRegionsDirty)) &#123;</span><br><span class="line">        ATRACE_NAME(<span class="string">"rebuildLayerStacks VR Dirty"</span>);</span><br><span class="line">        mVisibleRegionsDirty = <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">//重置mGeometryInvalid标记</span></span><br><span class="line">        invalidateHwcGeometry();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//遍历每个屏幕，因为每个Display是分开合成的</span></span><br><span class="line">        <span class="comment">//根据显示屏的特性，分别进行合成，合成后的数据也送给各自的显示屏</span></span><br><span class="line">        <span class="comment">//mDisplays是当前系统中的显示屏</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; pair : mDisplays) &#123;</span><br><span class="line">            ......</span><br><span class="line">            <span class="keyword">if</span> (displayState.isEnabled) &#123;</span><br><span class="line">                <span class="comment">//计算屏幕的脏区域、每个Layer的可见区域、被覆盖的区域、可见非透明区域（见下面一小节）</span></span><br><span class="line">                computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);</span><br><span class="line">                <span class="comment">//正序遍历mDrawingState合成列表的layer</span></span><br><span class="line">                <span class="comment">//和当前的显示设备进行比较，Layer的脏区域是否在显示设备的显示区域内</span></span><br><span class="line">                <span class="comment">//如果在显示区域内的话说明该layer是需要更新的，则更新到显示设备的`VisibleLayersSortedByZ`列表中，等待被合成</span></span><br><span class="line">                mDrawingState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">                    <span class="comment">//</span></span><br><span class="line">                    <span class="keyword">auto</span> compositionLayer = layer-&gt;getCompositionLayer();</span><br><span class="line">                    <span class="keyword">if</span> (compositionLayer == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">const</span> <span class="keyword">auto</span> displayId = displayDevice-&gt;getId();</span><br><span class="line">                    sp&lt;compositionengine::LayerFE&gt; layerFE = compositionLayer-&gt;getLayerFE();</span><br><span class="line">                    LOG_ALWAYS_FATAL_IF(layerFE.get() == <span class="literal">nullptr</span>);</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">bool</span> needsOutputLayer = <span class="literal">false</span>;</span><br><span class="line">                    <span class="comment">//计算Layer需要绘制的区域drawRegion</span></span><br><span class="line">                    <span class="keyword">if</span> (display-&gt;belongsInOutput(layer-&gt;getLayerStack(),</span><br><span class="line">                                                 layer-&gt;getPrimaryDisplayOnly())) &#123;</span><br><span class="line">                        Region drawRegion(tr.transform(</span><br><span class="line">                                layer-&gt;visibleNonTransparentRegion));</span><br><span class="line">                        <span class="comment">//将Layer的可见区域和Display大小做交集</span></span><br><span class="line">                        drawRegion.andSelf(bounds);</span><br><span class="line">                        <span class="keyword">if</span> (!drawRegion.isEmpty()) &#123;</span><br><span class="line">                            needsOutputLayer = <span class="literal">true</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">//drawRegion不为空，将该Layer加到当前Display的Layer列表中</span></span><br><span class="line">                    <span class="keyword">if</span> (needsOutputLayer) &#123;</span><br><span class="line">                        layersSortedByZ.emplace_back(</span><br><span class="line">                                display-&gt;getOrCreateOutputLayer(displayId, compositionLayer,</span><br><span class="line">                                                                layerFE));</span><br><span class="line">                        deprecated_layersSortedByZ.add(layer);</span><br><span class="line"></span><br><span class="line">                        <span class="keyword">auto</span>&amp; outputLayerState = layersSortedByZ.back()-&gt;editState();</span><br><span class="line">                        outputLayerState.visibleRegion =</span><br><span class="line">                                tr.transform(layer-&gt;visibleRegion.intersect(displayState.viewport));</span><br><span class="line">                    <span class="comment">//之前Layer可见，现在不可见，将销毁掉HWC Layer</span></span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (displayId) &#123;</span><br><span class="line">                        <span class="comment">//对于正在从HWC Display中移除的和已经排队的帧的那些Layer，将他们添加到一个发布的Layer列表中，以便可以设置一个fence</span></span><br><span class="line">                        <span class="keyword">bool</span> hasExistingOutputLayer =</span><br><span class="line">                                display-&gt;getOutputLayerForLayer(compositionLayer.get()) != <span class="literal">nullptr</span>;</span><br><span class="line">                        <span class="keyword">bool</span> hasQueuedFrames = <span class="built_in">std</span>::find(mLayersWithQueuedFrames.cbegin(),</span><br><span class="line">                                                         mLayersWithQueuedFrames.cend(),</span><br><span class="line">                                                         layer) != mLayersWithQueuedFrames.cend();</span><br><span class="line"></span><br><span class="line">                        <span class="keyword">if</span> (hasExistingOutputLayer &amp;&amp; hasQueuedFrames) &#123;</span><br><span class="line">                            <span class="comment">//销毁掉的Layer放置到layersNeedingFences中</span></span><br><span class="line">                            <span class="comment">//虽然不需要releaseFence，但是还是需要fence去释放旧的Buffer</span></span><br><span class="line">                            layersNeedingFences.add(layer);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;); <span class="comment">//遍历Layer结束</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//最后将数据更新到DisplayDevice中</span></span><br><span class="line">            display-&gt;setOutputLayersOrderedByZ(<span class="built_in">std</span>::move(layersSortedByZ));</span><br><span class="line"></span><br><span class="line">            displayDevice-&gt;setVisibleLayersSortedByZ(deprecated_layersSortedByZ);</span><br><span class="line">            displayDevice-&gt;setLayersNeedingFences(layersNeedingFences);</span><br><span class="line"></span><br><span class="line">            Region undefinedRegion&#123;bounds&#125;;</span><br><span class="line">            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));</span><br><span class="line"></span><br><span class="line">            display-&gt;editState().undefinedRegion = undefinedRegion;</span><br><span class="line">            display-&gt;editState().dirtyRegion.orSelf(dirtyRegion);</span><br><span class="line">        &#125;<span class="comment">//遍历Display结束</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="computeVisibleRegions计算可见区域"><a href="#computeVisibleRegions计算可见区域" class="headerlink" title="computeVisibleRegions计算可见区域"></a>computeVisibleRegions计算可见区域</h4><p>代码包含注解：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//outOpaqueRegion是屏幕的非透明区域</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::computeVisibleRegions</span><span class="params">(<span class="keyword">const</span> sp&lt;<span class="keyword">const</span> DisplayDevice&gt;&amp; displayDevice,</span></span></span><br><span class="line"><span class="function"><span class="params">                                           Region&amp; outDirtyRegion, Region&amp; outOpaqueRegion)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"computeVisibleRegions"</span>);</span><br><span class="line">    <span class="comment">//获取当前合成显示屏的display</span></span><br><span class="line">    <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">    <span class="comment">//针对当前整个Display！</span></span><br><span class="line">    <span class="comment">//当前Layer上层所有Layer不透明区域的累加</span></span><br><span class="line">    Region aboveOpaqueLayers;</span><br><span class="line">    <span class="comment">//当前Layer上层所有Layer可见区域的累加</span></span><br><span class="line">    Region aboveCoveredLayers;</span><br><span class="line">    <span class="comment">//脏区域</span></span><br><span class="line">    Region dirty;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//清空屏幕脏区域（每个Layer脏区域的和），将计算好的区域值设置到Layer中</span></span><br><span class="line">    outDirtyRegion.clear();</span><br><span class="line">    <span class="comment">//反序号遍历，即从最上面layer层开始遍历</span></span><br><span class="line">    mDrawingState.traverseInReverseZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">        <span class="comment">// start with the whole surface at its current location</span></span><br><span class="line">        <span class="keyword">const</span> Layer::State&amp; s(layer-&gt;getDrawingState());</span><br><span class="line">        <span class="comment">// layerStackId必须匹配</span></span><br><span class="line">        <span class="keyword">if</span> (!display-&gt;belongsInOutput(layer-&gt;getLayerStack(), layer-&gt;getPrimaryDisplayOnly())) &#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">//针对每个Layer！</span></span><br><span class="line">        <span class="comment">//完全不透明区域</span></span><br><span class="line">        Region opaqueRegion;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//可见区域（屏幕上可见的Surface区域，而且是不完全透明）</span></span><br><span class="line">        <span class="comment">//包含半透明区域：半透明Surface覆盖的区域被视为可见</span></span><br><span class="line">        Region visibleRegion;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//覆盖区域：被全部覆盖的Surface区域（包括被透明区域覆盖的区域）</span></span><br><span class="line">        Region coveredRegion;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//完全透明区域：Surface完全透明的部分，如果没有可见的非透明区域，这个Layer就可以从Layer列表中删除</span></span><br><span class="line">        <span class="comment">//并且不会影响该Layer本身或其下方layer的可见区域大小</span></span><br><span class="line">        <span class="comment">//这个区域可能不太准，因为有的APP不遵守SurfaceView的限制</span></span><br><span class="line">        Region transparentRegion;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通过将可见区域设置为空来处理隐藏的Surface</span></span><br><span class="line">        <span class="keyword">if</span> (CC_LIKELY(layer-&gt;isVisible())) &#123;</span><br><span class="line">            <span class="comment">//isOpaque表示Lyaer是非透明的Layer（上层应用层设置）</span></span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">bool</span> translucent = !layer-&gt;isOpaque(s);</span><br><span class="line">            <span class="comment">//获取Layer在屏幕上的大小</span></span><br><span class="line">            <span class="comment">//注：activeWidth和activeHeight是Layer本身的大小,用win表示</span></span><br><span class="line">            <span class="comment">//crop是Layer的源剪截区域，由上层设置，表示该Layer只截取crop的区域进行合成显示。可能比win大也可能小，需要取交集，截取重复的部分</span></span><br><span class="line">            Rect bounds(layer-&gt;getScreenBounds());</span><br><span class="line">            <span class="comment">//即上面返回Layer大小设置为可见区域，但是后续还会被裁剪</span></span><br><span class="line">            visibleRegion.<span class="built_in">set</span>(bounds);</span><br><span class="line">            <span class="comment">//Layer的变换矩阵，例如旋转，适配显示屏幕</span></span><br><span class="line">            ui::Transform tr = layer-&gt;getTransform();</span><br><span class="line">            <span class="comment">//可见区域不为空（一般情况下，如果layer是非透明的，非透明区域就是可见区域）</span></span><br><span class="line">            <span class="keyword">if</span> (!visibleRegion.isEmpty()) &#123;</span><br><span class="line">                <span class="comment">// 从可见区域移除完全透明区域</span></span><br><span class="line">                <span class="keyword">if</span> (translucent) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (tr.preserveRects()) &#123;</span><br><span class="line">                        <span class="comment">// transform the transparent region</span></span><br><span class="line">                        transparentRegion = tr.transform(layer-&gt;getActiveTransparentRegion(s));</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="comment">//转型太复杂，不能做到透明区域优化</span></span><br><span class="line">                        transparentRegion.clear();</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">const</span> <span class="keyword">int32_t</span> layerOrientation = tr.getOrientation();</span><br><span class="line">                <span class="keyword">if</span> (layer-&gt;getAlpha() == <span class="number">1.0f</span> &amp;&amp; !translucent &amp;&amp;</span><br><span class="line">                        layer-&gt;getRoundedCornerState().radius == <span class="number">0.0f</span> &amp;&amp;</span><br><span class="line">                        ((layerOrientation &amp; ui::Transform::ROT_INVALID) == <span class="literal">false</span>)) &#123;</span><br><span class="line">                    <span class="comment">// the opaque region is the layer's footprint</span></span><br><span class="line">                    opaqueRegion = visibleRegion;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">// 将覆盖区域剪辑到可见区域</span></span><br><span class="line">        <span class="comment">//遍历时，第一层时，aboveCoveredLayers为空，coveredRegion也是为空，最上面一层是没有被覆盖的，当然为空</span></span><br><span class="line">        coveredRegion = aboveCoveredLayers.intersect(visibleRegion);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//为下一个（底）layer更新aboveCoveredLayers</span></span><br><span class="line">        <span class="comment">//更新aboveCoveredLayers，该层之下的Layer都被该层Layer覆盖，所以这里和可见区域做一个或操纵，最下面的区域被覆盖的越大</span></span><br><span class="line">        aboveCoveredLayers.orSelf(visibleRegion);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//减去在我aboveOpaqueLayers覆盖的不透明区域</span></span><br><span class="line">        <span class="comment">//可见区域要减掉该层之上的非透明区域</span></span><br><span class="line">        visibleRegion.subtractSelf(aboveOpaqueLayers);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 计算Layer的脏区域</span></span><br><span class="line">        <span class="comment">// contentDirty表示包含脏区域内容，即Layer的可见区域被修改了</span></span><br><span class="line">        <span class="keyword">if</span> (layer-&gt;contentDirty) &#123;</span><br><span class="line">            <span class="comment">//需要使整个地区无效</span></span><br><span class="line">            dirty = visibleRegion;</span><br><span class="line">            <span class="comment">//以及旧的可见区域</span></span><br><span class="line">            dirty.orSelf(layer-&gt;visibleRegion);</span><br><span class="line">            layer-&gt;contentDirty = <span class="literal">false</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//计算暴露出来的区域 exposedRegion</span></span><br><span class="line">            <span class="comment">//包含两部分：1.之前被覆盖的区域，现在可见了；2.现在暴露的比之前的少</span></span><br><span class="line">            <span class="comment">//注：1是从整体的可见区域开始，但是只保留以前被覆盖的区域（现在暴露了）；2是处理那种因为重新调整了大小从而暴露出来的区域</span></span><br><span class="line">            <span class="keyword">const</span> Region newExposed = visibleRegion - coveredRegion;</span><br><span class="line">            <span class="keyword">const</span> Region oldVisibleRegion = layer-&gt;visibleRegion;</span><br><span class="line">            <span class="keyword">const</span> Region oldCoveredRegion = layer-&gt;coveredRegion;</span><br><span class="line">            <span class="keyword">const</span> Region oldExposed = oldVisibleRegion - oldCoveredRegion;</span><br><span class="line">            dirty = (visibleRegion&amp;oldCoveredRegion) | (newExposed-oldExposed);</span><br><span class="line">        &#125;</span><br><span class="line">        dirty.subtractSelf(aboveOpaqueLayers);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//累计到屏幕脏区域</span></span><br><span class="line">        outDirtyRegion.orSelf(dirty);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//更新opaqueRegion到aboveOpaqueLayers，为下面（底）的Layer做准备</span></span><br><span class="line">        aboveOpaqueLayers.orSelf(opaqueRegion);</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">        layer-&gt;setVisibleRegion(visibleRegion);</span><br><span class="line">        <span class="comment">//设置被覆盖的区域</span></span><br><span class="line">        layer-&gt;setCoveredRegion(coveredRegion);</span><br><span class="line">        <span class="comment">//设置可见的非透明区域（=可见区域-透明区域）</span></span><br><span class="line">        layer-&gt;setVisibleNonTransparentRegion(</span><br><span class="line">                visibleRegion.subtract(transparentRegion));</span><br><span class="line">    &#125;);  <span class="comment">//遍历结束</span></span><br><span class="line">    <span class="comment">//屏幕的非透明区域</span></span><br><span class="line">    outOpaqueRegion = aboveOpaqueLayers;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="calculateWorkingSet"><a href="#calculateWorkingSet" class="headerlink" title="calculateWorkingSet"></a>calculateWorkingSet</h3><p>在Android P中是用的<code>setUpHWComposer</code>函数，Q升级后将其分成几个单独的函数。以下是第一个：</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::calculateWorkingSet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(__FUNCTION__);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建H/W工作列表（判断变量在rebuildLayerStacks中变动）</span></span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(mGeometryInvalid)) &#123;</span><br><span class="line">        <span class="comment">//重置</span></span><br><span class="line">        mGeometryInvalid = <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">//遍历Display</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; [token, displayDevice] : mDisplays) &#123;</span><br><span class="line">            <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">uint32_t</span> zOrder = <span class="number">0</span>;</span><br><span class="line">            <span class="comment">//遍历Layer</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : display-&gt;getOutputLayersOrderedByZ()) &#123;</span><br><span class="line">                <span class="comment">//**调用CompositionEngine/src/OutputLayer.cpp返回mState</span></span><br><span class="line">                <span class="keyword">auto</span>&amp; compositionState = layer-&gt;editState();</span><br><span class="line">                <span class="comment">//forceClientComposition指强制GPU合成（Client）</span></span><br><span class="line">                <span class="comment">//mDebugDisableHWC指开发者选项的“停用HWC叠加层”，mDebugRegion指调试Region</span></span><br><span class="line">                compositionState.forceClientComposition = <span class="literal">false</span>;</span><br><span class="line">                <span class="keyword">if</span> (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) &#123;</span><br><span class="line">                    compositionState.forceClientComposition = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">//输出的z顺序值是一个简单的计数器设置</span></span><br><span class="line">                compositionState.z = zOrder++;</span><br><span class="line"></span><br><span class="line">                <span class="comment">//更新Display自己的合成状态</span></span><br><span class="line">                layer-&gt;getLayerFE().latchCompositionState(layer-&gt;getLayer().editState().frontEnd,</span><br><span class="line">                                                          <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//重新计算输出output layer的合成状态</span></span><br><span class="line">                layer-&gt;updateCompositionState(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//写入到HWC，该函数会设置Layer的几何尺寸（见下一小节该函数释义）</span></span><br><span class="line">                layer-&gt;writeStateToHWC(<span class="literal">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 class="comment">//设置每层Layer的frame帧数据</span></span><br><span class="line">    <span class="comment">//遍历Display</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; [token, displayDevice] : mDisplays) &#123;</span><br><span class="line">        <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">auto</span> displayId = display-&gt;getId();</span><br><span class="line">        <span class="keyword">if</span> (!displayId) &#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">auto</span>* profile = display-&gt;getDisplayColorProfile();</span><br><span class="line">        <span class="comment">//设置颜色矩阵</span></span><br><span class="line">        <span class="keyword">if</span> (mDrawingState.colorMatrixChanged) &#123;</span><br><span class="line">            display-&gt;setColorTransform(mDrawingState.colorMatrix);</span><br><span class="line">        &#125;</span><br><span class="line">        Dataspace targetDataspace = Dataspace::UNKNOWN;</span><br><span class="line">        <span class="keyword">if</span> (useColorManagement) &#123;</span><br><span class="line">            ColorMode colorMode;</span><br><span class="line">            RenderIntent renderIntent;</span><br><span class="line">            pickColorMode(displayDevice, &amp;colorMode, &amp;targetDataspace, &amp;renderIntent);</span><br><span class="line">            <span class="comment">//设置色彩模式</span></span><br><span class="line">            display-&gt;setColorMode(colorMode, targetDataspace, renderIntent);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//遍历可见layer</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : displayDevice-&gt;getVisibleLayersSortedByZ()) &#123;</span><br><span class="line">            <span class="comment">//根据layer的数据空间dataSpace（通过dump SurfaceFlinger可以查看到）来设置layer的合成方式</span></span><br><span class="line">            <span class="keyword">if</span> (layer-&gt;isHdrY410()) &#123;</span><br><span class="line">                layer-&gt;forceClientComposition(displayDevice);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((layer-&gt;getDataSpace() == Dataspace::BT2020_PQ ||</span><br><span class="line">                        layer-&gt;getDataSpace() == Dataspace::BT2020_ITU_PQ) &amp;&amp;</span><br><span class="line">                       !profile-&gt;hasHDR10Support()) &#123;</span><br><span class="line">                layer-&gt;forceClientComposition(displayDevice);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((layer-&gt;getDataSpace() == Dataspace::BT2020_HLG ||</span><br><span class="line">                        layer-&gt;getDataSpace() == Dataspace::BT2020_ITU_HLG) &amp;&amp;</span><br><span class="line">                       !profile-&gt;hasHLGSupport()) &#123;</span><br><span class="line">                layer-&gt;forceClientComposition(displayDevice);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (layer-&gt;getRoundedCornerState().radius &gt; <span class="number">0.0f</span>) &#123;</span><br><span class="line">                layer-&gt;forceClientComposition(displayDevice);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (layer-&gt;getForceClientComposition(displayDevice)) &#123;</span><br><span class="line">                ALOGV(<span class="string">"[%s] Requesting Client composition"</span>, layer-&gt;getName().<span class="built_in">string</span>());</span><br><span class="line">                <span class="comment">//设置合成方式GPU合成</span></span><br><span class="line">                layer-&gt;setCompositionType(displayDevice,</span><br><span class="line">                                          Hwc2::IComposerClient::Composition::CLIENT);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//设置每一层Layer的显示数据</span></span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">auto</span>&amp; displayState = display-&gt;getState();</span><br><span class="line">            layer-&gt;setPerFrameData(displayDevice, displayState.transform, displayState.viewport,</span><br><span class="line">                                   displayDevice-&gt;getSupportedPerFrameMetadata(),</span><br><span class="line">                                   isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN</span><br><span class="line">                                                                          : targetDataspace);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mDrawingState.colorMatrixChanged = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; [token, displayDevice] : mDisplays) &#123;</span><br><span class="line">        <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : displayDevice-&gt;getVisibleLayersSortedByZ()) &#123;</span><br><span class="line">            <span class="keyword">auto</span>&amp; layerState = layer-&gt;getCompositionLayer()-&gt;editState().frontEnd;</span><br><span class="line">            layerState.compositionType = <span class="keyword">static_cast</span>&lt;Hwc2::IComposerClient::Composition&gt;(</span><br><span class="line">                    layer-&gt;getCompositionType(displayDevice));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>颜色矩阵如下，可以在开发这选项中设置，有<code>模拟颜色空间</code>选项。其支持的transform主要有：</strong></p><figure class="highlight cpp"><figcaption><span>system/core/libsystem/include/system/graphics-base-v1.0.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">typedef</span> <span class="keyword">enum</span> &#123;</span><br><span class="line">    HAL_COLOR_TRANSFORM_IDENTITY = <span class="number">0</span>,</span><br><span class="line">    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = <span class="number">1</span>,</span><br><span class="line">    HAL_COLOR_TRANSFORM_VALUE_INVERSE = <span class="number">2</span>,</span><br><span class="line">    HAL_COLOR_TRANSFORM_GRAYSCALE = <span class="number">3</span>,</span><br><span class="line">    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = <span class="number">4</span>,</span><br><span class="line">    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = <span class="number">5</span>,</span><br><span class="line">    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = <span class="number">6</span>,</span><br><span class="line">&#125; <span class="keyword">android_color_transform_t</span>;</span><br></pre></td></tr></table></figure><h4 id="writeStateToHWC设置Layer几何尺寸"><a href="#writeStateToHWC设置Layer几何尺寸" class="headerlink" title="writeStateToHWC设置Layer几何尺寸"></a>writeStateToHWC设置Layer几何尺寸</h4><p>OutputLayer.cpp是Android新分离出来的文件，writeStateToHWC函数也是分离成一个单独的函数，以供SurfaceFlinger的calculateWorkingSet函数调用。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">OutputLayer::writeStateToHWC</span><span class="params">(<span class="keyword">bool</span> includeGeometry)</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Skip doing this if there is no HWC interface</span></span><br><span class="line">    <span class="comment">//此处的State数据是来源于DrawingState</span></span><br><span class="line">    <span class="keyword">if</span> (!mState.hwc) &#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">auto</span>&amp; hwcLayer = (*mState.hwc).hwcLayer;</span><br><span class="line">    <span class="keyword">if</span> (!hwcLayer) &#123;</span><br><span class="line">        ALOGE(<span class="string">"[%s] failed to write composition state to HWC -- no hwcLayer for output %s"</span>,</span><br><span class="line">              mLayerFE-&gt;getDebugName(), mOutput.getName().c_str());</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> (includeGeometry) &#123;</span><br><span class="line">        <span class="comment">//输出依赖状态 Output dependent state</span></span><br><span class="line">        <span class="comment">//计算DisplayFrame</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> error = hwcLayer-&gt;setDisplayFrame(mState.displayFrame);</span><br><span class="line">            error != HWC2::Error::None) &#123;... <span class="comment">//log打印</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//计算SourceCrop</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> error = hwcLayer-&gt;setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) &#123;</span><br><span class="line">            ... <span class="comment">//log打印</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//设置zOrder</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> error = hwcLayer-&gt;setZOrder(mState.z); error != HWC2::Error::None) &#123;</span><br><span class="line">            ... <span class="comment">//log打印</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//设置transform旋转</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> error =</span><br><span class="line">                    hwcLayer-&gt;setTransform(<span class="keyword">static_cast</span>&lt;HWC2::Transform&gt;(mState.bufferTransform));</span><br><span class="line">            error != HWC2::Error::None) &#123;</span><br><span class="line">            ... <span class="comment">//log打印</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//输出独立状态 Output independent state</span></span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">auto</span>&amp; outputIndependentState = mLayer-&gt;getState().frontEnd;</span><br><span class="line">        <span class="comment">//设置图层混合模式（见https://developer.android.google.cn/reference/android/graphics/BlendMode）</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> error = hwcLayer-&gt;setBlendMode(</span><br><span class="line">                    <span class="keyword">static_cast</span>&lt;HWC2::BlendMode&gt;(outputIndependentState.blendMode));</span><br><span class="line">            error != HWC2::Error::None) &#123;</span><br><span class="line">            ... <span class="comment">//log打印</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//设置Alpha透明度</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> error = hwcLayer-&gt;setPlaneAlpha(outputIndependentState.alpha);</span><br><span class="line">            error != HWC2::Error::None) &#123;</span><br><span class="line">            ... <span class="comment">//log打印</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//设置Layer信息</span></span><br><span class="line">        <span class="comment">//type和appId是Android Framework层创建SurfaceControl时设置的</span></span><br><span class="line">        <span class="comment">//其中type包含ScreenshotSurface、Background等</span></span><br><span class="line">        <span class="comment">//appId是应用进程号</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> error =</span><br><span class="line">                    hwcLayer-&gt;setInfo(outputIndependentState.type, outputIndependentState.appId);</span><br><span class="line">            error != HWC2::Error::None) &#123;</span><br><span class="line">            A... <span class="comment">//log打印</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>其中<code>setBlendModes</code>设置混合模式（两个Layer直接的混合方式），主要有以下几种：</p><figure class="highlight cpp"><figcaption><span>hardware/libhardware/include/hardware/hwcomposer2.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">/* Blend modes, settable per layer */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">enum</span> &#123;</span><br><span class="line">    HWC2_BLEND_MODE_INVALID = <span class="number">0</span>, </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* colorOut = colorSrc */</span></span><br><span class="line">    HWC2_BLEND_MODE_NONE = <span class="number">1</span>,  <span class="comment">//不混合，源和输出不变</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* colorOut = colorSrc + colorDst * (1 - alphaSrc) */</span></span><br><span class="line">    HWC2_BLEND_MODE_PREMULTIPLIED = <span class="number">2</span>,  <span class="comment">//预乘，Dst需要做Alpha的处理</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* colorOut = colorSrc * alphaSrc + colorDst * (1 - alphaSrc) */</span></span><br><span class="line">    HWC2_BLEND_MODE_COVERAGE = <span class="number">3</span>, <span class="comment">//覆盖方式，源和Dst都需要做Alpha透明度的处理</span></span><br><span class="line">&#125; <span class="keyword">hwc2_blend_mode_t</span>;</span><br></pre></td></tr></table></figure><h4 id="setPerFrameData设置每一层Layer显示数据"><a href="#setPerFrameData设置每一层Layer显示数据" class="headerlink" title="setPerFrameData设置每一层Layer显示数据"></a>setPerFrameData设置每一层Layer显示数据</h4><ol><li><code>ColorLayer::setPerFrameData</code></li></ol><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/ColorLayer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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"><span class="function"><span class="keyword">void</span> <span class="title">ColorLayer::setPerFrameData</span><span class="params">(<span class="keyword">const</span> sp&lt;<span class="keyword">const</span> DisplayDevice&gt;&amp; display,</span></span></span><br><span class="line"><span class="function"><span class="params">                                 <span class="keyword">const</span> ui::Transform&amp; transform, <span class="keyword">const</span> Rect&amp; viewport,</span></span></span><br><span class="line"><span class="function"><span class="params">                                 <span class="keyword">int32_t</span> <span class="comment">/* supportedPerFrameMetadata */</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                                 <span class="keyword">const</span> ui::Dataspace targetDataspace)</span> </span>&#123;</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//设置可见区域，之前已经计算好，但是此处需要确保可见区域在Display的窗口内</span></span><br><span class="line">    <span class="keyword">auto</span> error = hwcLayer-&gt;setVisibleRegion(visible);</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//设置数据空间</span></span><br><span class="line">    error = hwcLayer-&gt;setDataspace(dataspace);</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span>&amp; layerCompositionState = getCompositionLayer()-&gt;editState().frontEnd;</span><br><span class="line">    layerCompositionState.dataspace = mCurrentDataSpace;</span><br><span class="line">    <span class="comment">//设置RGB颜色，Alpha默认255（全透明）</span></span><br><span class="line">    half4 color = getColor();</span><br><span class="line">    error = hwcLayer-&gt;setColor(&#123;<span class="keyword">static_cast</span>&lt;<span class="keyword">uint8_t</span>&gt;(<span class="built_in">std</span>::round(<span class="number">255.0f</span> * color.r)),</span><br><span class="line">                                <span class="keyword">static_cast</span>&lt;<span class="keyword">uint8_t</span>&gt;(<span class="built_in">std</span>::round(<span class="number">255.0f</span> * color.g)),</span><br><span class="line">                                <span class="keyword">static_cast</span>&lt;<span class="keyword">uint8_t</span>&gt;(<span class="built_in">std</span>::round(<span class="number">255.0f</span> * color.b)), <span class="number">255</span>&#125;);</span><br><span class="line">    ...</span><br><span class="line">    layerCompositionState.color = &#123;<span class="keyword">static_cast</span>&lt;<span class="keyword">uint8_t</span>&gt;(<span class="built_in">std</span>::round(<span class="number">255.0f</span> * color.r)),</span><br><span class="line">                                   <span class="keyword">static_cast</span>&lt;<span class="keyword">uint8_t</span>&gt;(<span class="built_in">std</span>::round(<span class="number">255.0f</span> * color.g)),</span><br><span class="line">                                   <span class="keyword">static_cast</span>&lt;<span class="keyword">uint8_t</span>&gt;(<span class="built_in">std</span>::round(<span class="number">255.0f</span> * color.b)), <span class="number">255</span>&#125;;</span><br><span class="line">    <span class="comment">//色彩ColorLayer不需要变换矩阵，清除掉</span></span><br><span class="line">    error = hwcLayer-&gt;setTransform(HWC2::Transform::None);</span><br><span class="line">    <span class="keyword">if</span> (error != HWC2::Error::None) &#123;</span><br><span class="line">        ALOGE(<span class="string">"[%s] Failed to clear transform: %s (%d)"</span>, mName.<span class="built_in">string</span>(), to_string(error).c_str(),</span><br><span class="line">              <span class="keyword">static_cast</span>&lt;<span class="keyword">int32_t</span>&gt;(error));</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    error = hwcLayer-&gt;setColorTransform(getColorTransform());</span><br><span class="line">    <span class="keyword">if</span> (error != HWC2::Error::None) &#123;</span><br><span class="line">        ALOGE(<span class="string">"[%s] Failed to setColorTransform: %s (%d)"</span>, mName.<span class="built_in">string</span>(),</span><br><span class="line">                to_string(error).c_str(), <span class="keyword">static_cast</span>&lt;<span class="keyword">int32_t</span>&gt;(error));</span><br><span class="line">    &#125;</span><br><span class="line">    layerCompositionState.colorTransform = getColorTransform();</span><br><span class="line"></span><br><span class="line">    error = hwcLayer-&gt;setSurfaceDamage(surfaceDamageRegion);</span><br><span class="line">    <span class="keyword">if</span> (error != HWC2::Error::None) &#123;</span><br><span class="line">        ALOGE(<span class="string">"[%s] Failed to set surface damage: %s (%d)"</span>, mName.<span class="built_in">string</span>(),</span><br><span class="line">              to_string(error).c_str(), <span class="keyword">static_cast</span>&lt;<span class="keyword">int32_t</span>&gt;(error));</span><br><span class="line">        surfaceDamageRegion.dump(LOG_TAG);</span><br><span class="line">    &#125;</span><br><span class="line">    layerCompositionState.surfaceDamage = surfaceDamageRegion;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li><code>BufferLayer::setPerFrameData</code></li></ol><p>BufferLayer的处理比ColorLayer多，Sideband，Cursor和其他的UI图层都属于BufferLayer，每种类型Layer处理都不同。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/BufferLayer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="function"><span class="keyword">void</span> <span class="title">BufferLayer::setPerFrameData</span><span class="params">(<span class="keyword">const</span> sp&lt;<span class="keyword">const</span> DisplayDevice&gt;&amp; displayDevice,</span></span></span><br><span class="line"><span class="function"><span class="params">                                  <span class="keyword">const</span> ui::Transform&amp; transform, <span class="keyword">const</span> Rect&amp; viewport,</span></span></span><br><span class="line"><span class="function"><span class="params">                                  <span class="keyword">int32_t</span> supportedPerFrameMetadata,</span></span></span><br><span class="line"><span class="function"><span class="params">                                  <span class="keyword">const</span> ui::Dataspace targetDataspace)</span> </span>&#123;</span><br><span class="line">    RETURN_IF_NO_HWC_LAYER(displayDevice);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//在给HWC HAL层前，设置Display的投影的viewport给可见区域</span></span><br><span class="line">    Region visible = transform.transform(visibleRegion.intersect(viewport));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span> outputLayer = findOutputLayerForDisplay(displayDevice);</span><br><span class="line">    LOG_FATAL_IF(!outputLayer || !outputLayer-&gt;getState().hwc);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span>&amp; hwcLayer = (*outputLayer-&gt;getState().hwc).hwcLayer;</span><br><span class="line">    <span class="comment">//设置可见区域</span></span><br><span class="line">    <span class="keyword">auto</span> error = hwcLayer-&gt;setVisibleRegion(visible);</span><br><span class="line">    outputLayer-&gt;editState().visibleRegion = visible;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span>&amp; layerCompositionState = getCompositionLayer()-&gt;editState().frontEnd;</span><br><span class="line">    <span class="comment">//设置Damage区域</span></span><br><span class="line">    error = hwcLayer-&gt;setSurfaceDamage(surfaceDamageRegion);</span><br><span class="line">    layerCompositionState.surfaceDamage = surfaceDamageRegion;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Sideband layers处理，默认为SIDEBAND合成</span></span><br><span class="line">    <span class="keyword">if</span> (layerCompositionState.sidebandStream.get()) &#123;</span><br><span class="line">        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::SIDEBAND);</span><br><span class="line">        ALOGV(<span class="string">"[%s] Requesting Sideband composition"</span>, mName.<span class="built_in">string</span>());</span><br><span class="line">        error = hwcLayer-&gt;setSidebandStream(layerCompositionState.sidebandStream-&gt;handle());</span><br><span class="line">        layerCompositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Device or Cursor layers</span></span><br><span class="line">    <span class="comment">//如果是Cursor Layer，则合成方式为CURSOR，其他为DEVICE合成</span></span><br><span class="line">    <span class="keyword">if</span> (mPotentialCursor) &#123;</span><br><span class="line">        ALOGV(<span class="string">"[%s] Requesting Cursor composition"</span>, mName.<span class="built_in">string</span>());</span><br><span class="line">        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CURSOR);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        ALOGV(<span class="string">"[%s] Requesting Device composition"</span>, mName.<span class="built_in">string</span>());</span><br><span class="line">        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ui::Dataspace dataspace = isColorSpaceAgnostic() &amp;&amp; targetDataspace != ui::Dataspace::UNKNOWN</span><br><span class="line">            ? targetDataspace</span><br><span class="line">            : mCurrentDataSpace;</span><br><span class="line">    <span class="comment">//设置数据空间</span></span><br><span class="line">    error = hwcLayer-&gt;setDataspace(dataspace);</span><br><span class="line">    <span class="comment">//HDR</span></span><br><span class="line">    <span class="keyword">const</span> HdrMetadata&amp; metadata = getDrawingHdrMetadata();</span><br><span class="line">    error = hwcLayer-&gt;setPerFrameMetadata(supportedPerFrameMetadata, metadata);</span><br><span class="line">    <span class="comment">//设置色彩矩阵</span></span><br><span class="line">    error = hwcLayer-&gt;setColorTransform(getColorTransform());</span><br><span class="line">    <span class="keyword">if</span> (error == HWC2::Error::Unsupported) &#123;</span><br><span class="line">        <span class="comment">//如果每个layer的色彩矩阵都不支持，则使用GPU合成（CLIENT）</span></span><br><span class="line">        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CLIENT);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (error != HWC2::Error::None) &#123;</span><br><span class="line">        ALOGE(<span class="string">"[%s] Failed to setColorTransform: %s (%d)"</span>, mName.<span class="built_in">string</span>(),</span><br><span class="line">                to_string(error).c_str(), <span class="keyword">static_cast</span>&lt;<span class="keyword">int32_t</span>&gt;(error));</span><br><span class="line">    &#125;</span><br><span class="line">    layerCompositionState.dataspace = mCurrentDataSpace;</span><br><span class="line">    layerCompositionState.colorTransform = getColorTransform();</span><br><span class="line">    layerCompositionState.hdrMetadata = metadata;</span><br><span class="line"></span><br><span class="line">    setHwcLayerBuffer(displayDevice);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="beginFrame"><a href="#beginFrame" class="headerlink" title="beginFrame"></a>beginFrame</h3><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">SurfaceFlinger::beginFrame</span><span class="params">(<span class="keyword">const</span> sp&lt;DisplayDevice&gt;&amp; displayDevice)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span>&amp; displayState = display-&gt;getState();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> dirty = !display-&gt;getDirtyRegion(<span class="literal">false</span>).isEmpty();</span><br><span class="line">    <span class="keyword">bool</span> empty = displayDevice-&gt;getVisibleLayersSortedByZ().size() == <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">bool</span> wasEmpty = !displayState.lastCompositionHadVisibleLayers;</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">//如果有一些变化，但是当前没有任何可见的Layers，并且最近一次的合成也没有，则跳过这次合成；</span></span><br><span class="line">    <span class="comment">//第二条判断做了以下两件事：</span></span><br><span class="line">    <span class="comment">//1.当所有layers从该Display被移除，我们将发射一个黑色（无内容）的帧，然后没有其他任何东西，直到我们获取到新的layers；</span></span><br><span class="line">    <span class="comment">//2.当一个Display被创建，包含了一个单独的空layer栈，我们将不会发射任何黑色的帧，知道一个Layers被添加到这个layer栈</span></span><br><span class="line">    <span class="keyword">bool</span> mustRecompose = dirty &amp;&amp; !(empty &amp;&amp; wasEmpty);</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//主显、外显：fsurfaceflinger/DisplayHardware/FramebufferSurface.h的beginFrame</span></span><br><span class="line">    <span class="comment">//虚拟显示：surfaceflinger/DisplayHardware/VirtualDisplaySurface.h的beginFrame</span></span><br><span class="line">    display-&gt;getRenderSurface()-&gt;beginFrame(mustRecompose);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mustRecompose) &#123;</span><br><span class="line">        display-&gt;editState().lastCompositionHadVisibleLayers = !empty;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="prepareFrame准备数据"><a href="#prepareFrame准备数据" class="headerlink" title="prepareFrame准备数据"></a>prepareFrame准备数据</h3><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::prepareFrame</span><span class="params">(<span class="keyword">const</span> sp&lt;DisplayDevice&gt;&amp; displayDevice)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span>&amp; displayState = display-&gt;getState();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!displayState.isEnabled) &#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">status_t</span> result = display-&gt;getRenderSurface()-&gt;prepareFrame();</span><br><span class="line">    ALOGE_IF(result != NO_ERROR, <span class="string">"prepareFrame failed for %s: %d (%s)"</span>,</span><br><span class="line">             displayDevice-&gt;getDebugName().c_str(), result, strerror(-result));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">status_t</span> <span class="title">RenderSurface::prepareFrame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">auto</span>&amp; hwc = mCompositionEngine.getHwComposer();</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span> id = mDisplay.getId();</span><br><span class="line">    <span class="keyword">if</span> (id) &#123;</span><br><span class="line">        <span class="comment">//查看HWC是否支持之前SurfaceFlinger决定的合成方式</span></span><br><span class="line">        <span class="keyword">status_t</span> error = hwc.prepare(*id, mDisplay);</span><br><span class="line">        <span class="keyword">if</span> (error != NO_ERROR) &#123;</span><br><span class="line">            <span class="keyword">return</span> error;</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">    DisplaySurface::CompositionType compositionType;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">bool</span> hasClient = hwc.hasClientComposition(id);</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">bool</span> hasDevice = hwc.hasDeviceComposition(id);</span><br><span class="line">    <span class="keyword">if</span> (hasClient &amp;&amp; hasDevice) &#123;</span><br><span class="line">        compositionType = DisplaySurface::COMPOSITION_MIXED;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (hasClient) &#123;</span><br><span class="line">        compositionType = DisplaySurface::COMPOSITION_GLES;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (hasDevice) &#123;</span><br><span class="line">        compositionType = DisplaySurface::COMPOSITION_HWC;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// Nothing to do -- when turning the screen off we get a frame like</span></span><br><span class="line">        <span class="comment">// this. Call it a HWC frame since we won't be doing any GLES work but</span></span><br><span class="line">        <span class="comment">// will do a prepare/set cycle.</span></span><br><span class="line">        compositionType = DisplaySurface::COMPOSITION_HWC;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> mDisplaySurface-&gt;prepareFrame(compositionType);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>重要函数：</strong></p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">HWComposer::prepare</span><span class="params">(DisplayId displayId, <span class="keyword">const</span> compositionengine::Output&amp; output)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    displayData.validateWasSkipped = <span class="literal">false</span>;</span><br><span class="line">    <span class="comment">//SurfaceFlinger没有指定得有Client端合成，即false</span></span><br><span class="line">    <span class="keyword">if</span> (!displayData.hasClientComposition) &#123;</span><br><span class="line">        sp&lt;Fence&gt; outPresentFence;</span><br><span class="line">        <span class="keyword">uint32_t</span> state = UINT32_MAX;</span><br><span class="line">        <span class="comment">//调用函数尝试直接present，如果HWC不能直接显示，再执行validate操纵</span></span><br><span class="line">        error = hwcDisplay-&gt;presentOrValidate(&amp;numTypes, &amp;numRequests, &amp;outPresentFence , &amp;state);</span><br><span class="line">        <span class="keyword">if</span> (error != HWC2::Error::HasChanges) &#123;</span><br><span class="line">            RETURN_IF_HWC_ERROR_FOR(<span class="string">"presentOrValidate"</span>, error, displayId, UNKNOWN_ERROR);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//如果成功，数据显示，则不再执行后续流程</span></span><br><span class="line">        <span class="keyword">if</span> (state == <span class="number">1</span>) &#123; <span class="comment">//Present Succeeded.</span></span><br><span class="line">            <span class="built_in">std</span>::<span class="built_in">unordered_map</span>&lt;HWC2::Layer*, sp&lt;Fence&gt;&gt; releaseFences;</span><br><span class="line">            error = hwcDisplay-&gt;getReleaseFences(&amp;releaseFences);</span><br><span class="line">            displayData.releaseFences = <span class="built_in">std</span>::move(releaseFences);</span><br><span class="line">            displayData.lastPresentFence = outPresentFence;</span><br><span class="line">            displayData.validateWasSkipped = <span class="literal">true</span>;</span><br><span class="line">            displayData.presentError = error;</span><br><span class="line">            <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// Present failed but Validate ran.</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//调用validate</span></span><br><span class="line">        error = hwcDisplay-&gt;validate(&amp;numTypes, &amp;numRequests);</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unordered_map</span>&lt;HWC2::Layer*, HWC2::Composition&gt; changedTypes;</span><br><span class="line">    changedTypes.reserve(numTypes);</span><br><span class="line">    <span class="comment">//通过getChangedCompositionTypes函数获取到HWC对合成方式的修改，保存在changedTypes中</span></span><br><span class="line">    error = hwcDisplay-&gt;getChangedCompositionTypes(&amp;changedTypes);</span><br><span class="line">    RETURN_IF_HWC_ERROR_FOR(<span class="string">"getChangedCompositionTypes"</span>, error, displayId, BAD_INDEX);</span><br><span class="line"></span><br><span class="line">    displayData.displayRequests = <span class="keyword">static_cast</span>&lt;HWC2::DisplayRequest&gt;(<span class="number">0</span>);</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unordered_map</span>&lt;HWC2::Layer*, HWC2::LayerRequest&gt; layerRequests;</span><br><span class="line">    layerRequests.reserve(numRequests);</span><br><span class="line">    <span class="comment">//获取LayerRequest，保存在layerRequests中</span></span><br><span class="line">    error = hwcDisplay-&gt;getRequests(&amp;displayData.displayRequests,</span><br><span class="line">            &amp;layerRequests);</span><br><span class="line">    RETURN_IF_HWC_ERROR_FOR(<span class="string">"getRequests"</span>, error, displayId, BAD_INDEX);</span><br><span class="line">    .....</span><br><span class="line">    <span class="comment">//合成方式初始化为false</span></span><br><span class="line">    displayData.hasClientComposition = <span class="literal">false</span>;</span><br><span class="line">    displayData.hasDeviceComposition = <span class="literal">false</span>;</span><br><span class="line">    <span class="comment">//遍历Layer</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; outputLayer : output.getOutputLayersOrderedByZ()) &#123;</span><br><span class="line">        <span class="keyword">auto</span>&amp; state = outputLayer-&gt;editState();</span><br><span class="line">        LOG_FATAL_IF(!state.hwc.);</span><br><span class="line">        <span class="keyword">auto</span> hwcLayer = (*state.hwc).hwcLayer;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> it = changedTypes.find(hwcLayer.get()); it != changedTypes.end()) &#123;</span><br><span class="line">            <span class="keyword">auto</span> newCompositionType = it-&gt;second;</span><br><span class="line">            validateChange(<span class="keyword">static_cast</span>&lt;HWC2::Composition&gt;((*state.hwc).hwcCompositionType),</span><br><span class="line">                           newCompositionType);</span><br><span class="line">            (*state.hwc).hwcCompositionType =</span><br><span class="line">                    <span class="keyword">static_cast</span>&lt;Hwc2::IComposerClient::Composition&gt;(newCompositionType);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">switch</span> ((*state.hwc).hwcCompositionType) &#123;</span><br><span class="line">            <span class="keyword">case</span> Hwc2::IComposerClient::Composition::CLIENT:</span><br><span class="line">                displayData.hasClientComposition = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> Hwc2::IComposerClient::Composition::DEVICE:</span><br><span class="line">            <span class="keyword">case</span> Hwc2::IComposerClient::Composition::SOLID_COLOR:</span><br><span class="line">            <span class="keyword">case</span> Hwc2::IComposerClient::Composition::CURSOR:</span><br><span class="line">            <span class="keyword">case</span> Hwc2::IComposerClient::Composition::SIDEBAND:</span><br><span class="line">                displayData.hasDeviceComposition = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//响应layerRequests</span></span><br><span class="line">        state.clearClientTarget = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">auto</span> it = layerRequests.find(hwcLayer.get()); it != layerRequests.end()) &#123;</span><br><span class="line">            <span class="keyword">auto</span> request = it-&gt;second;</span><br><span class="line">            <span class="keyword">if</span> (request == HWC2::LayerRequest::ClearClientTarget) &#123;</span><br><span class="line">                state.clearClientTarget = <span class="literal">true</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                LOG_DISPLAY_ERROR(displayId,</span><br><span class="line">                                  (<span class="string">"Unknown layer request "</span> + to_string(request)).c_str());</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">//最后，通过HWC，SurfaceFlinger接受修改</span></span><br><span class="line">    error = hwcDisplay-&gt;acceptChanges();</span><br><span class="line">    RETURN_IF_HWC_ERROR_FOR(<span class="string">"acceptChanges"</span>, error, displayId, BAD_INDEX);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>validate刷新：</strong></p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/DisplayHardware/HWC2.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">Error <span class="title">Display::validate</span><span class="params">(<span class="keyword">uint32_t</span>* outNumTypes, <span class="keyword">uint32_t</span>* outNumRequests)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">uint32_t</span> numTypes = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">uint32_t</span> numRequests = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//调用Composer::validateDisplay</span></span><br><span class="line">    <span class="keyword">auto</span> intError = mComposer.validateDisplay(mId, &amp;numTypes, &amp;numRequests);</span><br><span class="line">    <span class="keyword">auto</span> error = <span class="keyword">static_cast</span>&lt;Error&gt;(intError);</span><br><span class="line">    <span class="keyword">if</span> (error != Error::None &amp;&amp; error != Error::HasChanges) &#123;</span><br><span class="line">        <span class="keyword">return</span> error;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    *outNumTypes = numTypes;</span><br><span class="line">    *outNumRequests = numRequests;</span><br><span class="line">    <span class="keyword">return</span> error;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>validateDisplay</code>是通过CommandWriter写Buffer的方式调用到HWC中的，但是多了一个<code>execute</code>函数：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/DisplayHardware/ComposerHal.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">Error <span class="title">Composer::validateDisplay</span><span class="params">(Display display, <span class="keyword">uint32_t</span>* outNumTypes,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">uint32_t</span>* outNumRequests)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    mWriter.selectDisplay(display);</span><br><span class="line">    <span class="comment">//Buffer命令的调用，只是将命令写到Buffer中</span></span><br><span class="line">    mWriter.validateDisplay();</span><br><span class="line">    <span class="comment">//真正的将触发HWC服务端解析Buffer命令，再分别取调HWC对应的实现函数</span></span><br><span class="line">    Error error = execute();</span><br><span class="line">    <span class="keyword">if</span> (error != Error::NONE) &#123;</span><br><span class="line">        <span class="keyword">return</span> error;</span><br><span class="line">    &#125;</span><br><span class="line">    mReader.hasChanges(display, outNumTypes, outNumRequests);</span><br><span class="line">    <span class="keyword">return</span> Error::NONE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="doDebugFlashRegions"><a href="#doDebugFlashRegions" class="headerlink" title="doDebugFlashRegions"></a>doDebugFlashRegions</h3><p>doDebugFlashRegions只是一个debug功能，受<code>mDebugRegion</code>控制（开发者选项）。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">SurfaceFlinger::doDebugFlashRegions</span><span class="params">(<span class="keyword">const</span> sp&lt;DisplayDevice&gt;&amp; displayDevice,</span></span></span><br><span class="line"><span class="function"><span class="params">                                         <span class="keyword">bool</span> repaintEverything)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span>&amp; displayState = display-&gt;getState();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// is debugging enabled</span></span><br><span class="line">    <span class="keyword">if</span> (CC_LIKELY(!mDebugRegion))</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (displayState.isEnabled) &#123;</span><br><span class="line">        <span class="comment">// transform the dirty region into this screen's coordinate space</span></span><br><span class="line">        <span class="keyword">const</span> Region dirtyRegion = display-&gt;getDirtyRegion(repaintEverything);</span><br><span class="line">        <span class="keyword">if</span> (!dirtyRegion.isEmpty()) &#123;</span><br><span class="line">            base::unique_fd readyFence;</span><br><span class="line">            <span class="comment">// redraw the whole screen</span></span><br><span class="line">            doComposeSurfaces(displayDevice, dirtyRegion, &amp;readyFence);</span><br><span class="line">            display-&gt;getRenderSurface()-&gt;queueBuffer(<span class="built_in">std</span>::move(readyFence));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    postFramebuffer(displayDevice);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mDebugRegion &gt; <span class="number">1</span>) &#123;</span><br><span class="line">        usleep(mDebugRegion * <span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    prepareFrame(displayDevice);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="doComposition合成处理"><a href="#doComposition合成处理" class="headerlink" title="doComposition合成处理"></a>doComposition合成处理</h3><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">SurfaceFlinger::doComposition</span><span class="params">(<span class="keyword">const</span> sp&lt;DisplayDevice&gt;&amp; displayDevice, <span class="keyword">bool</span> repaintEverything)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"doComposition"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span>&amp; displayState = display-&gt;getState();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (displayState.isEnabled) &#123;</span><br><span class="line">        <span class="comment">//获取rebuildLayerStacks时计算的当前显示设备的脏区域DirtyRegion</span></span><br><span class="line">        <span class="comment">//如果是强制重画，mRepaintEverything为true，那么脏区域就是整个屏幕的大小</span></span><br><span class="line">        <span class="comment">//1. 获取脏区域</span></span><br><span class="line">        <span class="keyword">const</span> Region dirtyRegion = display-&gt;getDirtyRegion(repaintEverything);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// repaint the framebuffer (if needed)</span></span><br><span class="line">        <span class="comment">//2. 主要是对当前的显示设备上的所有不支持硬件合成Layer进行OpenGL合成处理</span></span><br><span class="line">        doDisplayComposition(displayDevice, dirtyRegion);</span><br><span class="line"></span><br><span class="line">        display-&gt;editState().dirtyRegion.clear();</span><br><span class="line">        display-&gt;getRenderSurface()-&gt;flip();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//统一交由HWC，由HWC硬件合成并输出到显示屏</span></span><br><span class="line">    postFramebuffer(displayDevice);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><p><strong>合成方式：</strong></p><ul><li><p>Client合成<br>Client合成方式是相对与硬件合成来说的，其合成方式是，将各个Layer的内容用GPU渲染到暂存缓冲区中，最后将暂存缓冲区传送到显示硬件。这个暂存缓冲区，我们称为FBTarget，每个Display设备有各自的FBTarget。Client合成，之前称为GLES合成，我们也可以称之为GPU合成。Client合成，采用RenderEngine进行合成。</p></li><li><p>Device合成<br>就是用专门的硬件合成器进行合成HWComposer，所以硬件合成的能力就取决于硬件的实现。其合成方式是将各个Layer的数据全部传给显示硬件，并告知它从不同的缓冲区读取屏幕不同部分的数据。HWComposer是Devicehec的抽象。</p></li></ul><hr><h4 id="getDirtyRegion获取脏区域"><a href="#getDirtyRegion获取脏区域" class="headerlink" title="getDirtyRegion获取脏区域"></a>getDirtyRegion获取脏区域</h4><p>前面在<code>rebuildLayerStacks</code>重构Layer的时候，Display的脏区域DirtyRegion已经计算出来。如果重画，则mRepaintEverything为true，此时脏区域就是整个屏幕的大小。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">Region <span class="title">Output::getDirtyRegion</span><span class="params">(<span class="keyword">bool</span> repaintEverything)</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="function">Region <span class="title">dirty</span><span class="params">(mState.viewport)</span></span>;</span><br><span class="line">    <span class="keyword">if</span> (!repaintEverything) &#123;</span><br><span class="line">        dirty.andSelf(mState.dirtyRegion);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dirty;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="doDisplayComposition合成"><a href="#doDisplayComposition合成" class="headerlink" title="doDisplayComposition合成"></a>doDisplayComposition合成</h4><p>合成方式主要就两种，一种Client客户端用GPU合成；另外一种，Device端HWC硬件合成。<code>doComposeSurfaces</code>主要是处理Client端合成，通过<code>RenderEngine</code>用GPU来进行合成。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::doDisplayComposition</span><span class="params">(<span class="keyword">const</span> sp&lt;DisplayDevice&gt;&amp; displayDevice,</span></span></span><br><span class="line"><span class="function"><span class="params">                                          <span class="keyword">const</span> Region&amp; inDirtyRegion)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">    <span class="comment">// 只有在以下情况下才去真正合成这个Display的图像:</span></span><br><span class="line">    <span class="comment">// 1) 需要HWC硬件合成</span></span><br><span class="line">    <span class="comment">// 2) 脏区域不为空</span></span><br><span class="line">    <span class="keyword">if</span> (!displayDevice-&gt;getId() &amp;&amp; inDirtyRegion.isEmpty()) &#123;</span><br><span class="line">        ALOGV(<span class="string">"Skipping display composition"</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">    ALOGV(<span class="string">"doDisplayComposition"</span>);</span><br><span class="line">    base::unique_fd readyFence;</span><br><span class="line">    <span class="comment">//调用doComposeSurfaces进行合成</span></span><br><span class="line">    <span class="keyword">if</span> (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &amp;readyFence)) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将合成后的Buffer提交给该显示设备的BufferQueue,最终有FrameBufferSurface进行处理</span></span><br><span class="line">    <span class="comment">//swap buffers (presentation)</span></span><br><span class="line">    <span class="comment">//代码路径：frameworks/native/libs/gui/Surface.cpp</span></span><br><span class="line">    display-&gt;getRenderSurface()-&gt;queueBuffer(<span class="built_in">std</span>::move(readyFence));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="doComposeSurfaces"><a href="#doComposeSurfaces" class="headerlink" title="*doComposeSurfaces"></a>*doComposeSurfaces</h5><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//doComposeSurfaces</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">SurfaceFlinger::doComposeSurfaces</span><span class="params">(<span class="keyword">const</span> sp&lt;DisplayDevice&gt;&amp; displayDevice,</span></span></span><br><span class="line"><span class="function"><span class="params">                                       <span class="keyword">const</span> Region&amp; debugRegion, base::unique_fd* readyFence)</span> </span>&#123;</span><br><span class="line">    .....</span><br><span class="line">    <span class="comment">//RenderEngine初始化</span></span><br><span class="line">    <span class="function"><span class="keyword">const</span> Region <span class="title">bounds</span><span class="params">(displayState.bounds)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">const</span> DisplayRenderArea <span class="title">renderArea</span><span class="params">(displayDevice)</span></span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">bool</span> hasClientComposition = getHwComposer().hasClientComposition(displayId);</span><br><span class="line">    ATRACE_INT(<span class="string">"hasClientComposition"</span>, hasClientComposition);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> applyColorMatrix = <span class="literal">false</span>;</span><br><span class="line">    renderengine::DisplaySettings clientCompositionDisplay;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;renderengine::LayerSettings&gt; clientCompositionLayers;</span><br><span class="line">    sp&lt;GraphicBuffer&gt; buf;</span><br><span class="line">    base::unique_fd fd;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (hasClientComposition) &#123;</span><br><span class="line">        ALOGV(<span class="string">"hasClientComposition"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (displayDevice-&gt;isPrimary() &amp;&amp; supportProtectedContent) &#123;</span><br><span class="line">            <span class="keyword">bool</span> needsProtected = <span class="literal">false</span>;</span><br><span class="line">            <span class="comment">//遍历layer</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : displayDevice-&gt;getVisibleLayersSortedByZ()) &#123;</span><br><span class="line">                <span class="comment">//如果layer受保护，则进行标记</span></span><br><span class="line">                <span class="keyword">if</span> (layer-&gt;isProtected()) &#123;</span><br><span class="line">                    needsProtected = <span class="literal">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">            <span class="keyword">if</span> (needsProtected != renderEngine.isProtected()) &#123;</span><br><span class="line">                renderEngine.useProtectedContext(needsProtected);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (needsProtected != display-&gt;getRenderSurface()-&gt;isProtected() &amp;&amp;</span><br><span class="line">                needsProtected == renderEngine.isProtected()) &#123;</span><br><span class="line">                display-&gt;getRenderSurface()-&gt;setProtected(needsProtected);</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">//Client合成Display的属性赋值</span></span><br><span class="line">        clientCompositionDisplay.physicalDisplay = displayState.scissor; <span class="comment">//主屏剪切区</span></span><br><span class="line">        clientCompositionDisplay.clip = displayState.scissor;</span><br><span class="line">        <span class="keyword">const</span> ui::Transform&amp; displayTransform = displayState.transform;</span><br><span class="line">        clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();</span><br><span class="line">        clientCompositionDisplay.orientation = displayState.orientation;  <span class="comment">//屏幕转向</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">auto</span>* profile = display-&gt;getDisplayColorProfile();</span><br><span class="line">        Dataspace outputDataspace = Dataspace::UNKNOWN;</span><br><span class="line">        <span class="comment">//是否用WideColor，设置数据空间</span></span><br><span class="line">        <span class="keyword">if</span> (profile-&gt;hasWideColorGamut()) &#123;</span><br><span class="line">            outputDataspace = displayState.dataspace;</span><br><span class="line">        &#125;</span><br><span class="line">        clientCompositionDisplay.outputDataspace = outputDataspace;</span><br><span class="line">        clientCompositionDisplay.maxLuminance =</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">if</span> (applyColorMatrix) &#123;</span><br><span class="line">            clientCompositionDisplay.colorTransform = displayState.colorTransformMat;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将Client端Layer渲染到FrameBuffer（即FBTarget）</span></span><br><span class="line">    ALOGV(<span class="string">"Rendering client layers"</span>);</span><br><span class="line">    <span class="keyword">bool</span> firstLayer = <span class="literal">true</span>;</span><br><span class="line">    Region clearRegion = Region::INVALID_REGION;</span><br><span class="line">    <span class="comment">//遍历layer</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : displayDevice-&gt;getVisibleLayersSortedByZ()) &#123;</span><br><span class="line">        <span class="function"><span class="keyword">const</span> Region <span class="title">viewportRegion</span><span class="params">(displayState.viewport)</span></span>;</span><br><span class="line">        <span class="function"><span class="keyword">const</span> Region <span class="title">clip</span><span class="params">(viewportRegion.intersect(layer-&gt;visibleRegion))</span></span>;</span><br><span class="line">        <span class="keyword">if</span> (!clip.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">switch</span> (layer-&gt;getCompositionType(displayDevice)) &#123;</span><br><span class="line">                <span class="keyword">case</span> Hwc2::IComposerClient::Composition::CURSOR:</span><br><span class="line">                <span class="keyword">case</span> Hwc2::IComposerClient::Composition::DEVICE:</span><br><span class="line">                <span class="keyword">case</span> Hwc2::IComposerClient::Composition::SIDEBAND:</span><br><span class="line">                <span class="keyword">case</span> Hwc2::IComposerClient::Composition::SOLID_COLOR: &#123;</span><br><span class="line">                    LOG_ALWAYS_FATAL_IF(!displayId);</span><br><span class="line">                    <span class="function"><span class="keyword">const</span> Layer::State&amp; <span class="title">state</span><span class="params">(layer-&gt;getDrawingState())</span></span>;</span><br><span class="line">                    <span class="keyword">if</span> (layer-&gt;getClearClientTarget(displayDevice) &amp;&amp; !firstLayer &amp;&amp;</span><br><span class="line">                        layer-&gt;isOpaque(state) &amp;&amp; (layer-&gt;getAlpha() == <span class="number">1.0f</span>) &amp;&amp;</span><br><span class="line">                        layer-&gt;getRoundedCornerState().radius == <span class="number">0.0f</span> &amp;&amp; hasClientComposition) &#123;</span><br><span class="line">                        <span class="comment">//千万不要清除第一层，因为我们保证FB已经清除了</span></span><br><span class="line">                        renderengine::LayerSettings layerSettings;</span><br><span class="line">                        Region dummyRegion;</span><br><span class="line">                        <span class="comment">//调用该函数，此处有对受保护secure layer的处理（如果有数字保护协议，则会显示black layer）</span></span><br><span class="line">                        <span class="comment">//然后会将layer的clip区域绘制到FBTarget上</span></span><br><span class="line">                        <span class="keyword">bool</span> prepared =</span><br><span class="line">                                layer-&gt;prepareClientLayer(renderArea, clip, dummyRegion,</span><br><span class="line">                                                          supportProtectedContent, layerSettings);</span><br><span class="line"></span><br><span class="line">                        <span class="keyword">if</span> (prepared) &#123;</span><br><span class="line">                            layerSettings.source.buffer.buffer = <span class="literal">nullptr</span>;</span><br><span class="line">                            layerSettings.source.solidColor = half3(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>);</span><br><span class="line">                            layerSettings.alpha = half(<span class="number">0.0</span>);</span><br><span class="line">                            layerSettings.disableBlending = <span class="literal">true</span>;</span><br><span class="line">                            clientCompositionLayers.push_back(layerSettings);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> Hwc2::IComposerClient::Composition::CLIENT: &#123;</span><br><span class="line">                    renderengine::LayerSettings layerSettings;</span><br><span class="line">                    <span class="comment">//同上</span></span><br><span class="line">                    <span class="keyword">bool</span> prepared =</span><br><span class="line">                            layer-&gt;prepareClientLayer(renderArea, clip, clearRegion,</span><br><span class="line">                                                      supportProtectedContent, layerSettings);</span><br><span class="line">                    <span class="keyword">if</span> (prepared) &#123;</span><br><span class="line">                        clientCompositionLayers.push_back(layerSettings);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            ALOGV(<span class="string">"  Skipping for empty clip"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        firstLayer = <span class="literal">false</span>;</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><h4 id="postFramebuffer"><a href="#postFramebuffer" class="headerlink" title="postFramebuffer"></a>postFramebuffer</h4><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::postFramebuffer</span><span class="params">(<span class="keyword">const</span> sp&lt;DisplayDevice&gt;&amp; displayDevice)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"postFramebuffer"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span>&amp; displayState = display-&gt;getState();</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span> displayId = display-&gt;getId();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (displayState.isEnabled) &#123;</span><br><span class="line">        <span class="keyword">if</span> (displayId) &#123;</span><br><span class="line">            <span class="comment">//调用该函数获取releaseFence</span></span><br><span class="line">            getHwComposer().presentAndGetReleaseFences(*displayId);</span><br><span class="line">        &#125;</span><br><span class="line">        display-&gt;getRenderSurface()-&gt;onPresentDisplayCompleted();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : display-&gt;getOutputLayersOrderedByZ()) &#123;</span><br><span class="line">            sp&lt;Fence&gt; releaseFence = Fence::NO_FENCE;</span><br><span class="line">            <span class="keyword">bool</span> usedClientComposition = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//在这一帧的fence发出信号的时候，上一帧的layer buffer被HWC释放</span></span><br><span class="line">            <span class="comment">//始终先从HWC处获取释放fence</span></span><br><span class="line">            <span class="keyword">if</span> (layer-&gt;getState().hwc) &#123;</span><br><span class="line">                <span class="keyword">const</span> <span class="keyword">auto</span>&amp; hwcState = *layer-&gt;getState().hwc;</span><br><span class="line">                releaseFence =</span><br><span class="line">                        getHwComposer().getLayerReleaseFence(*displayId, hwcState.hwcLayer.get());</span><br><span class="line">                usedClientComposition =</span><br><span class="line">                        hwcState.hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//如果层在上一帧中是Client客户端合成的，则需要与前一个Client客户端获取的fence合并。</span></span><br><span class="line">            <span class="comment">//因为我们不跟踪它，所以当它可用时，总是与当前客户端Fence合并，即使这是子选项</span></span><br><span class="line">            <span class="keyword">if</span> (usedClientComposition) &#123;</span><br><span class="line">                releaseFence =</span><br><span class="line">                        Fence::merge(<span class="string">"LayerRelease"</span>, releaseFence,</span><br><span class="line">                                     display-&gt;getRenderSurface()-&gt;getClientTargetAcquireFence());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            layer-&gt;getLayerFE().onLayerDisplayed(releaseFence);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//我们有一列需要fence的layer，它们与display-&gt;getVisibleLayersSortedByZ不相交。</span></span><br><span class="line">        <span class="comment">//我们能做的最好的就是给他们提供现有的fence</span></span><br><span class="line">        <span class="keyword">if</span> (!displayDevice-&gt;getLayersNeedingFences().isEmpty()) &#123;</span><br><span class="line">            sp&lt;Fence&gt; presentFence =</span><br><span class="line">                    displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : displayDevice-&gt;getLayersNeedingFences()) &#123;</span><br><span class="line">                layer-&gt;getCompositionLayer()-&gt;getLayerFE()-&gt;onLayerDisplayed(presentFence);</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> (displayId) &#123;</span><br><span class="line">            <span class="comment">//清除</span></span><br><span class="line">            getHwComposer().clearReleaseFences(*displayId);</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><hr><h3 id="postComposition"><a href="#postComposition" class="headerlink" title="postComposition"></a>postComposition</h3><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::postComposition</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"postComposition"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//调用Layer的onPostComposition， 处理Layer中刚刚绘制的Buffer的Fence</span></span><br><span class="line">    <span class="comment">//此时才会真正释放掉一个Buffer</span></span><br><span class="line">    <span class="keyword">nsecs_t</span> dequeueReadyTime = systemTime();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : mLayersWithQueuedFrames) &#123;</span><br><span class="line">        <span class="comment">//这一帧合成完成后，将会被替代的Buffer释放掉</span></span><br><span class="line">        layer-&gt;releasePendingBuffer(dequeueReadyTime);</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//记录Buffer状态</span></span><br><span class="line">    mDrawingState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">        <span class="keyword">bool</span> frameLatched =</span><br><span class="line">                layer-&gt;onPostComposition(displayDevice-&gt;getId(), glCompositionDoneFenceTime,</span><br><span class="line">                                         presentFenceTime, compositorTiming);</span><br><span class="line">        <span class="keyword">if</span> (frameLatched) &#123;</span><br><span class="line">            recordBufferingStats(layer-&gt;getName().<span class="built_in">string</span>(),</span><br><span class="line">                    layer-&gt;getOccupancyHistory(<span class="literal">false</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//VSYNC是由mScheduler分发出来的，并不是每一次都是从底层硬件上报的</span></span><br><span class="line">    <span class="comment">//所以mScheduler需要和底层硬件Vsync保持同步</span></span><br><span class="line">    <span class="keyword">if</span> (presentFenceTime-&gt;isValid()) &#123;</span><br><span class="line">        mScheduler-&gt;addPresentFence(presentFenceTime);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//Vsync同步</span></span><br><span class="line">    <span class="keyword">if</span> (!hasSyncFramework) &#123;</span><br><span class="line">        <span class="keyword">if</span> (displayDevice &amp;&amp; getHwComposer().isConnected(*displayDevice-&gt;getId()) &amp;&amp;</span><br><span class="line">            displayDevice-&gt;isPoweredOn()) &#123;</span><br><span class="line">            mScheduler-&gt;enableHardwareVsync();</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">if</span> (mAnimCompositionPending) &#123;</span><br><span class="line">        mAnimCompositionPending = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (presentFenceTime-&gt;isValid()) &#123;</span><br><span class="line">            mAnimFrameTracker.setActualPresentFence(</span><br><span class="line">                    <span class="built_in">std</span>::move(presentFenceTime));</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (displayDevice &amp;&amp; getHwComposer().isConnected(*displayDevice-&gt;getId())) &#123;</span><br><span class="line">            <span class="comment">// The HWC doesn't support present fences, so use the refresh</span></span><br><span class="line">            <span class="comment">// timestamp instead.</span></span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">nsecs_t</span> presentTime =</span><br><span class="line">                    getHwComposer().getRefreshTimestamp(*displayDevice-&gt;getId());</span><br><span class="line">            mAnimFrameTracker.setActualPresentTime(presentTime);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//advanceFrame处理FBTarget</span></span><br><span class="line">        mAnimFrameTracker.advanceFrame();</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//处理时间的记录</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>至此，REFRESH处理完成。之后等到下一个Vsync周期，开始下一次合成。</p><h4 id="advanceFrame"><a href="#advanceFrame" class="headerlink" title="advanceFrame"></a>advanceFrame</h4><p>其中调用advanceFrame方法，虚显用的<code>VirtualDisplaySurface</code>，非虚显用的<code>FramebufferSurface</code>。advanceFrame获取FBTarget的数据：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">status_t</span> <span class="title">FramebufferSurface::advanceFrame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">uint32_t</span> slot = <span class="number">0</span>;</span><br><span class="line">    sp&lt;GraphicBuffer&gt; buf;</span><br><span class="line">    <span class="function">sp&lt;Fence&gt; <span class="title">acquireFence</span><span class="params">(Fence::NO_FENCE)</span></span>;</span><br><span class="line">    Dataspace dataspace = Dataspace::UNKNOWN;</span><br><span class="line">    <span class="keyword">status_t</span> result = nextBuffer(slot, buf, acquireFence, dataspace);</span><br><span class="line">    mDataSpace = dataspace;</span><br><span class="line">    <span class="keyword">if</span> (result != NO_ERROR) &#123;</span><br><span class="line">        ALOGE(<span class="string">"error latching next FramebufferSurface buffer: %s (%d)"</span>,</span><br><span class="line">                strerror(-result), result);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">FramebufferSurface::nextBuffer</span><span class="params">(<span class="keyword">uint32_t</span>&amp; outSlot,</span></span></span><br><span class="line"><span class="function"><span class="params">        sp&lt;GraphicBuffer&gt;&amp; outBuffer, sp&lt;Fence&gt;&amp; outFence,</span></span></span><br><span class="line"><span class="function"><span class="params">        Dataspace&amp; outDataspace)</span> </span>&#123;</span><br><span class="line">    <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line">    BufferItem item;</span><br><span class="line">    <span class="comment">//1.获取一个Buffer（在上面doComposition - doDisplayComposition函数最后调用queueBuffer，会放到FrameBufferSurface的BufferQueue中）</span></span><br><span class="line">    <span class="comment">//此处的函数将从这个BufferQueue中获取一个Buffer</span></span><br><span class="line">    <span class="keyword">status_t</span> err = acquireBufferLocked(&amp;item, <span class="number">0</span>);</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//2.当前Buffer序号mCurrentBufferSlot，当前Buffer是mCurrentBuffer，对应的Fence是mCurrentFence</span></span><br><span class="line">    <span class="comment">//如果上面获取到的Buffer不一样，则将Current置为Previous上一个buffer，否则没有变化</span></span><br><span class="line">    <span class="keyword">if</span> (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT &amp;&amp;</span><br><span class="line">        item.mSlot != mCurrentBufferSlot) &#123;</span><br><span class="line">        mHasPendingRelease = <span class="literal">true</span>;</span><br><span class="line">        mPreviousBufferSlot = mCurrentBufferSlot;</span><br><span class="line">        mPreviousBuffer = mCurrentBuffer;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//3.将获取到的Buffer置为当前的Current</span></span><br><span class="line">    mCurrentBufferSlot = item.mSlot;</span><br><span class="line">    mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;</span><br><span class="line">    mCurrentFence = item.mFence;</span><br><span class="line"></span><br><span class="line">    outFence = item.mFence;</span><br><span class="line">    mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &amp;outSlot, &amp;outBuffer);</span><br><span class="line">    outDataspace = <span class="keyword">static_cast</span>&lt;Dataspace&gt;(item.mDataSpace);</span><br><span class="line">    <span class="comment">//4.将FBTarget设置给HWC  （头文件HWComposer&amp; mHwc;）</span></span><br><span class="line">    <span class="keyword">status_t</span> result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);</span><br><span class="line">    <span class="keyword">if</span> (result != NO_ERROR) &#123;</span><br><span class="line">        ALOGE(<span class="string">"error posting framebuffer: %d"</span>, result);</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> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>setClientTarget函数：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">status_t</span> <span class="title">HWComposer::setClientTarget</span><span class="params">(DisplayId displayId, <span class="keyword">uint32_t</span> slot,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     <span class="keyword">const</span> sp&lt;Fence&gt;&amp; acquireFence, <span class="keyword">const</span> sp&lt;GraphicBuffer&gt;&amp; target,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     ui::Dataspace dataspace)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//头文件：std::unordered_map&lt;DisplayId, DisplayData&gt; mDisplayData;</span></span><br><span class="line">    <span class="keyword">auto</span>&amp; hwcDisplay = mDisplayData[displayId].hwcDisplay;</span><br><span class="line">    <span class="comment">//FBTarget是通过Command Buffer的方式传到HWC中的</span></span><br><span class="line">    <span class="keyword">auto</span> error = hwcDisplay-&gt;setClientTarget(slot, target, acquireFence, dataspace);</span><br><span class="line">    <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>HWComposer头文件，hwcDisplay在HWC2命名空间内。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">DisplayData</span> &#123;</span></span><br><span class="line">    <span class="keyword">bool</span> isVirtual = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">bool</span> hasClientComposition = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">bool</span> hasDeviceComposition = <span class="literal">false</span>;</span><br><span class="line">    HWC2::Display* hwcDisplay = <span class="literal">nullptr</span>;</span><br><span class="line">    HWC2::DisplayRequest displayRequests;</span><br><span class="line">    sp&lt;Fence&gt; lastPresentFence = Fence::NO_FENCE; <span class="comment">// signals when the last set op retires</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unordered_map</span>&lt;HWC2::Layer*, sp&lt;Fence&gt;&gt; releaseFences;</span><br><span class="line">    <span class="keyword">buffer_handle_t</span> outbufHandle = <span class="literal">nullptr</span>;</span><br><span class="line">    sp&lt;Fence&gt; outbufAcquireFence = Fence::NO_FENCE;</span><br><span class="line">    <span class="keyword">mutable</span> <span class="built_in">std</span>::<span class="built_in">unordered_map</span>&lt;<span class="keyword">int32_t</span>,</span><br><span class="line">            <span class="built_in">std</span>::<span class="built_in">shared_ptr</span>&lt;<span class="keyword">const</span> HWC2::Display::Config&gt;&gt; configMap;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> validateWasSkipped;</span><br><span class="line">    HWC2::Error presentError;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> vsyncTraceToggle = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">std</span>::mutex vsyncEnabledLock;</span><br><span class="line">    <span class="function">HWC2::Vsync vsyncEnabled <span class="title">GUARDED_BY</span><span class="params">(vsyncEnabledLock)</span> </span>= HWC2::Vsync::Disable;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">mutable</span> <span class="built_in">std</span>::mutex lastHwVsyncLock;</span><br><span class="line">    <span class="function"><span class="keyword">nsecs_t</span> lastHwVsync <span class="title">GUARDED_BY</span><span class="params">(lastHwVsyncLock)</span> </span>= <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><hr><h2 id="GPU合成模块概述"><a href="#GPU合成模块概述" class="headerlink" title="GPU合成模块概述"></a>GPU合成模块概述</h2><p>硬件HWC合成是由Vendor实现。而各个厂商在这部分的实现不同。</p><p>GPU合成（Client）是Android原生自带的，本质是采用GPU进程合成，SurfaceFlinger模块封装了RenderEngine进行具体的实现。</p><p>看一下这个模块的文件目录：</p><figure class="highlight plain"><figcaption><span>frameworks/native/libs/renderengine</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">├── Android.bp</span><br><span class="line">├── Description.cpp</span><br><span class="line">├── gl</span><br><span class="line">│   ├── GLESRenderEngine.cpp</span><br><span class="line">│   ├── GLESRenderEngine.h</span><br><span class="line">│   ├── GLExtensions.cpp</span><br><span class="line">│   ├── GLExtensions.h</span><br><span class="line">│   ├── GLFramebuffer.cpp</span><br><span class="line">│   ├── GLFramebuffer.h</span><br><span class="line">│   ├── GLImage.cpp</span><br><span class="line">│   ├── GLImage.h</span><br><span class="line">│   ├── ProgramCache.cpp</span><br><span class="line">│   ├── ProgramCache.h</span><br><span class="line">│   ├── Program.cpp</span><br><span class="line">│   └── Program.h</span><br><span class="line">├── include</span><br><span class="line">│   └── renderengine</span><br><span class="line">│       ├── DisplaySettings.h</span><br><span class="line">│       ├── Framebuffer.h</span><br><span class="line">│       ├── Image.h</span><br><span class="line">│       ├── LayerSettings.h</span><br><span class="line">│       ├── Mesh.h</span><br><span class="line">│       ├── mock</span><br><span class="line">│       │   ├── Framebuffer.h</span><br><span class="line">│       │   ├── Image.h</span><br><span class="line">│       │   └── RenderEngine.h</span><br><span class="line">│       ├── private</span><br><span class="line">│       │   └── Description.h</span><br><span class="line">│       ├── RenderEngine.h</span><br><span class="line">│       └── Texture.h</span><br><span class="line">├── Mesh.cpp</span><br><span class="line">├── mock</span><br><span class="line">│   ├── Framebuffer.cpp</span><br><span class="line">│   ├── Image.cpp</span><br><span class="line">│   └── RenderEngine.cpp</span><br><span class="line">├── OWNERS</span><br><span class="line">├── RenderEngine.cpp</span><br><span class="line">├── TEST_MAPPING</span><br><span class="line">├── tests</span><br><span class="line">│   ├── Android.bp</span><br><span class="line">│   └── RenderEngineTest.cpp</span><br><span class="line">└── Texture.cpp</span><br></pre></td></tr></table></figure><h3 id="创建RenderEngine"><a href="#创建RenderEngine" class="headerlink" title="创建RenderEngine"></a>创建RenderEngine</h3><p>在SF.cpp初始化函数init中：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">// TODO(b/77156734): We need to stop casting and use HAL types when possible.</span></span><br><span class="line">    <span class="comment">// Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.</span></span><br><span class="line">    mCompositionEngine-&gt;setRenderEngine(</span><br><span class="line">            renderengine::RenderEngine::create(<span class="keyword">static_cast</span>&lt;<span class="keyword">int32_t</span>&gt;(defaultCompositionPixelFormat),</span><br><span class="line">                                               renderEngineFeature, maxFrameBufferAcquiredBuffers));</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>create调用RenderEngine.cpp中的对应函数：（在Q版本该模块已经独立出来，该模块是对GPU渲染的封装）</p><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/renderengine/RenderEngine.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;impl::RenderEngine&gt; <span class="title">RenderEngine::create</span><span class="params">(<span class="keyword">int</span> hwcFormat, <span class="keyword">uint32_t</span> featureFlags,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                         <span class="keyword">uint32_t</span> imageCacheSize)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">char</span> prop[PROPERTY_VALUE_MAX];</span><br><span class="line">    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, <span class="string">"gles"</span>);</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">strcmp</span>(prop, <span class="string">"gles"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        ALOGD(<span class="string">"RenderEngine GLES Backend"</span>);</span><br><span class="line">        <span class="keyword">return</span> renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize);</span><br><span class="line">    &#125;</span><br><span class="line">    ALOGE(<span class="string">"UNKNOWN BackendType: %s, create GLES RenderEngine."</span>, prop);</span><br><span class="line">    <span class="keyword">return</span> renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/renderengine/gl/GLESRenderEngine.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;GLESRenderEngine&gt; <span class="title">GLESRenderEngine::create</span><span class="params">(<span class="keyword">int</span> hwcFormat, <span class="keyword">uint32_t</span> featureFlags,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                           <span class="keyword">uint32_t</span> imageCacheSize)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//创建EGLDisplay</span></span><br><span class="line">    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);</span><br><span class="line">    <span class="comment">//初始化EGLDisplay</span></span><br><span class="line">    <span class="keyword">if</span> (!eglInitialize(display, <span class="literal">nullptr</span>, <span class="literal">nullptr</span>)) &#123;</span><br><span class="line">        LOG_ALWAYS_FATAL(<span class="string">"failed to initialize EGL"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//选择EGLConfig</span></span><br><span class="line">    EGLConfig config = EGL_NO_CONFIG;</span><br><span class="line">    <span class="keyword">if</span> (!extensions.hasNoConfigContext()) &#123;</span><br><span class="line">        config = chooseEglConfig(display, hwcFormat, <span class="comment">/*logConfig*/</span> <span class="literal">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//创建EGLContext</span></span><br><span class="line">    <span class="comment">//在该函数中:</span></span><br><span class="line">    <span class="comment">//1.调用eglGetConfigAttrib获取renderableType、</span></span><br><span class="line">    <span class="comment">//2.初始化Context属性contextAttributes、</span></span><br><span class="line">    <span class="comment">//3.调用eglCreateContext创建EGLContext</span></span><br><span class="line">    <span class="keyword">if</span> ((featureFlags &amp; RenderEngine::ENABLE_PROTECTED_CONTEXT) &amp;&amp;</span><br><span class="line">        extensions.hasProtectedContent()) &#123;</span><br><span class="line">        protectedContext = createEglContext(display, config, <span class="literal">nullptr</span>, useContextPriority,</span><br><span class="line">                                            Protection::PROTECTED);</span><br><span class="line">        ALOGE_IF(protectedContext == EGL_NO_CONTEXT, <span class="string">"Can't create protected context"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,</span><br><span class="line">                                       Protection::UNPROTECTED);</span><br><span class="line">    ...</span><br><span class="line">    EGLSurface dummy = EGL_NO_SURFACE;</span><br><span class="line">    <span class="keyword">if</span> (!extensions.hasSurfacelessContext()) &#123;</span><br><span class="line">        <span class="comment">//创建PBuffer</span></span><br><span class="line">        dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED);</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//查看可以获取到什么版本的GL</span></span><br><span class="line">    GlesVersion version = parseGlesVersion(extensions.getVersion());</span><br><span class="line"></span><br><span class="line">    <span class="comment">//初始化当前GL的渲染器RenderEngine</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;GLESRenderEngine&gt; engine;</span><br><span class="line">    <span class="keyword">switch</span> (version) &#123;</span><br><span class="line">        <span class="keyword">case</span> GLES_VERSION_1_0:</span><br><span class="line">        <span class="keyword">case</span> GLES_VERSION_1_1:</span><br><span class="line">            LOG_ALWAYS_FATAL(<span class="string">"SurfaceFlinger requires OpenGL ES 2.0 minimum to run."</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> GLES_VERSION_2_0:</span><br><span class="line">        <span class="keyword">case</span> GLES_VERSION_3_0:</span><br><span class="line">            engine = <span class="built_in">std</span>::make_unique&lt;GLESRenderEngine&gt;(featureFlags, display, config, ctxt, dummy,</span><br><span class="line">                                                        protectedContext, protectedDummy,</span><br><span class="line">                                                        imageCacheSize);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="创建Surface-FBTarget"><a href="#创建Surface-FBTarget" class="headerlink" title="创建Surface FBTarget"></a>创建Surface FBTarget</h3><p>RenderEngine创建时始化的EGLDisplaym，EGLConfig，EGLContext等，都是所有Display共用的。</p><p>而Surface每个Display的是自己的，在创建DisplayDevice时，创建对应的Surface。</p><p>从BufferQueue中dequeue Buffer进行渲染，swapBuffer时，也queue到Bufferqueu中。这里的ANativeWindow，本质就是FBTarget。</p><h3 id="创建Texture纹理"><a href="#创建Texture纹理" class="headerlink" title="创建Texture纹理"></a>创建Texture纹理</h3><p>在BufferLayer创建的构造函数中创建Texture：</p><figure class="highlight cpp"><figcaption><span>BufferLayer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">BufferLayer::BufferLayer(<span class="keyword">const</span> LayerCreationArgs&amp; args)</span><br><span class="line">      : Layer(args),</span><br><span class="line">      <span class="comment">//调用SurfaceFlinger的getNewTexture创建</span></span><br><span class="line">      <span class="comment">//在创建BufferLayerConsumer时，传到了Consumer中，对应的值为mTexName</span></span><br><span class="line">        mTextureName(args.flinger-&gt;getNewTexture()),</span><br><span class="line">        mCompositionLayer&#123;mFlinger-&gt;getCompositionEngine().createLayer(</span><br><span class="line">                compositionengine::LayerCreationArgs&#123;<span class="keyword">this</span>&#125;)&#125; &#123;</span><br><span class="line">                    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">uint32_t</span> <span class="title">SurfaceFlinger::getNewTexture</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">// The pool was empty, so we need to get a new texture name directly using a</span></span><br><span class="line">    <span class="comment">// blocking call to the main thread</span></span><br><span class="line">    <span class="keyword">uint32_t</span> name = <span class="number">0</span>;</span><br><span class="line">    postMessageSync(<span class="keyword">new</span> LambdaMessage([&amp;]() &#123; getRenderEngine().genTextures(<span class="number">1</span>, &amp;name); &#125;));</span><br><span class="line">    <span class="keyword">return</span> name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用genTextures：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/renderengine/gl/GLESRenderEngine.cpp</span></figcaption><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">void</span> <span class="title">GLESRenderEngine::genTextures</span><span class="params">(<span class="keyword">size_t</span> count, <span class="keyword">uint32_t</span>* names)</span> </span>&#123;</span><br><span class="line">    glGenTextures(count, names);  <span class="comment">//生成Texture，在BufferLayer中保存在mTexture中</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="绑定Texture纹理"><a href="#绑定Texture纹理" class="headerlink" title="绑定Texture纹理"></a>绑定Texture纹理</h3><p>在<code>BufferLayerConsumer::updateTexImage</code>函数中调用<code>bindTextureImageLocked</code>绑定新的buffer到GL Texture纹理。</p><p>而该函数是在SurfaceFlinger调用latchBuffer从BufferQueue申请获取渲染好的buffer的时候会调用到。</p><h3 id="Layer合成"><a href="#Layer合成" class="headerlink" title="Layer合成"></a>Layer合成</h3><p>在Q版本之前是调用的<code>onDraw</code>函数，而在Q上函数名变成<code>prepareClientLayer</code>。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">BufferLayer::prepareClientLayer</span><span class="params">(<span class="keyword">const</span> RenderArea&amp; renderArea, <span class="keyword">const</span> Region&amp; clip,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     <span class="keyword">bool</span> useIdentityTransform, Region&amp; clearRegion,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     <span class="keyword">const</span> <span class="keyword">bool</span> supportProtectedContent,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     renderengine::LayerSettings&amp; layer)</span> </span>&#123;</span><br><span class="line">                                        ...</span><br><span class="line">    <span class="comment">//DRM处理，是否阻塞当前Layer                                        </span></span><br><span class="line">    <span class="keyword">bool</span> blackOutLayer =</span><br><span class="line">            (isProtected() &amp;&amp; !supportProtectedContent) || (isSecure() &amp;&amp; !renderArea.isSecure());</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<code>SurfaceFlinger::doComposeSurfaces</code>函数中，调用完prepareClientLayer后，末尾最后调用<code>renderEngine.drawLayers</code>函数。</p><p>最终调用到GPU合成模块<code>frameworks/native/libs/renderengine/gl/GLESRenderEngine.cpp</code>的<code>GLESRenderEngine::drawLayers</code>函数。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">status_t</span> <span class="title">GLESRenderEngine::drawLayers</span><span class="params">(<span class="keyword">const</span> DisplaySettings&amp; display,</span></span></span><br><span class="line"><span class="function"><span class="params">                                      <span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;LayerSettings&gt;&amp; layers,</span></span></span><br><span class="line"><span class="function"><span class="params">                                      ANativeWindowBuffer* <span class="keyword">const</span> buffer,</span></span></span><br><span class="line"><span class="function"><span class="params">                                      <span class="keyword">const</span> <span class="keyword">bool</span> useFramebufferCache, base::unique_fd&amp;&amp; bufferFence,</span></span></span><br><span class="line"><span class="function"><span class="params">                                      base::unique_fd* drawFence)</span> </span>&#123;</span><br><span class="line">            ...</span><br><span class="line">            <span class="comment">//Texture坐标顶点</span></span><br><span class="line">            renderengine::<span class="function">Mesh::VertexArray&lt;vec2&gt; <span class="title">texCoords</span><span class="params">(mesh.getTexCoordArray&lt;vec2&gt;())</span></span>;</span><br><span class="line">            texCoords[<span class="number">0</span>] = vec2(<span class="number">0.0</span>, <span class="number">0.0</span>);</span><br><span class="line">            texCoords[<span class="number">1</span>] = vec2(<span class="number">0.0</span>, <span class="number">1.0</span>);</span><br><span class="line">            texCoords[<span class="number">2</span>] = vec2(<span class="number">1.0</span>, <span class="number">1.0</span>);</span><br><span class="line">            texCoords[<span class="number">3</span>] = vec2(<span class="number">1.0</span>, <span class="number">0.0</span>);</span><br><span class="line">            ...</span><br><span class="line">            <span class="comment">// Buffer sources will have a black solid color ignored in the shader,</span></span><br><span class="line">            <span class="comment">// so in that scenario the solid color passed here is arbitrary.</span></span><br><span class="line">            <span class="comment">//处理Alpha的Blend</span></span><br><span class="line">            setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,</span><br><span class="line">                               layer.geometry.roundedCornersRadius);</span><br><span class="line">                                      &#125;</span><br><span class="line">            ...</span><br><span class="line">            <span class="comment">// We only want to do a special handling for rounded corners when having rounded corners</span></span><br><span class="line">            <span class="comment">// is the only reason it needs to turn on blending, otherwise, we handle it like the</span></span><br><span class="line">            <span class="comment">// usual way since it needs to turn on blending anyway.</span></span><br><span class="line">            <span class="keyword">if</span> (layer.geometry.roundedCornersRadius &gt; <span class="number">0.0</span> &amp;&amp; color.a &gt;= <span class="number">1.0f</span> &amp;&amp; isOpaque) &#123;</span><br><span class="line">                handleRoundedCorners(display, layer, mesh);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">//绘制（合成）内容</span></span><br><span class="line">                <span class="comment">//该函数中使用glDrawArrays函数进行绘制（合成）</span></span><br><span class="line">                drawMesh(mesh);</span><br><span class="line">            &#125;</span><br></pre></td></tr></table></figure><hr><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><ul><li><a href="https://developer.android.google.cn/reference/android/graphics/BlendMode" target="_blank" rel="noopener">Google Developers - BlendMode</a></li><li><a href="https://www.jianshu.com/p/fa115146949f" target="_blank" rel="noopener">SurfaceFlinger合成流程(一)</a></li><li><a href="https://www.jianshu.com/p/fd16dcb4dfb6" target="_blank" rel="noopener">SurfaceFlinger合成流程(二)</a></li><li><a href="https://wizzie.top/Blog/2019/09/22/2019/190922-android-handler-cpp/#SurfaceFlinger%E7%9A%84%E6%B6%88%E6%81%AF%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6" target="_blank" rel="noopener">Android Handler消息循环处理机制</a></li><li><a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/" target="_blank" rel="noopener">Android 图形显示框架</a></li><li><a href="https://blog.csdn.net/u013686019/article/details/51614774" target="_blank" rel="noopener">Android BitTube</a></li><li><a href="https://blog.csdn.net/dabenxiong666/article/details/80629316" target="_blank" rel="noopener">Android之BitTube</a></li><li><a href="https://blog.csdn.net/weixin_41054077/article/details/105735639" target="_blank" rel="noopener">基于Android Q分析SurfaceFlinger启动过程</a></li><li><a href="https://wizzie.top/Blog/2019/12/22/2019/191222_android_HWC2/#mCurrentState%E5%92%8CmDrawingState" target="_blank" rel="noopener">Android SurfaceFlinger和HWC2概述 - mCurrentState和mDrawingState</a></li><li><a href="https://www.jianshu.com/p/b0928eaaeb1c" target="_blank" rel="noopener">SurfaceFlinger图像合成[1]</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;继上篇《Android　Ｑ SurfaceFlinger合成（一）》中SF对INVALIDATE信息处理，针对Layer属性变化、显示设备变化等情况处理，将mCurrentState提交到mDrawingState。然后遍历mDrawingState的Layer，将新的Buffer内容更新绑定到Layer纹理对象。经过这些流程，决定是否需要SF进行合成刷新，如果需要则调用&lt;code&gt;handleMessageRefresh&lt;/code&gt;开始合成处理。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android Q SurfaceFlinger合成（一）</title>
    <link href="https://alonealive.github.io/Blog/2020/10/15/2020/201015_android_SurfaceFlinger1/"/>
    <id>https://alonealive.github.io/Blog/2020/10/15/2020/201015_android_SurfaceFlinger1/</id>
    <published>2020-10-15T12:52:00.000Z</published>
    <updated>2020-10-15T12:44:00.005Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>在HWUI渲染完成后，Buffer会在<code>frameworks/native/libs/gui/BufferQueueProducer.cpp</code>中通过queueBuffer放入BufferQueue，在该函数中调用<code>frameAvailableListener-&gt;onFrameAvailable(item)</code>通知consumer消费者，从而触发SurfaceFlinger合成。</p></blockquote><a id="more"></a><h2 id="onFrameAvailable触发合成"><a href="#onFrameAvailable触发合成" class="headerlink" title="onFrameAvailable触发合成"></a>onFrameAvailable触发合成</h2><ol><li>上面的frameAvailableListener对象类型是<code>sp&lt;IConsumerListener&gt; frameAvailableListener;</code></li></ol><p>参考<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/" target="_blank" rel="noopener">Android 图形显示框架</a>中的《Surface创建流程》，在该流程中会创建BufferQueue，调用<code>BufferQueue::createBufferQueue</code>函数。</p><ol start="2"><li>而<code>class ProxyConsumerListener : public BnConsumerListener...</code>是IConsumerListener接口的Bn端，所以会调用到下面代码：</li></ol><p>BufferQueue中的frameAvailableListener，是一个IConsumerListener的接口，对应的Binder的Bn端实现为ProxyConsumerListener。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/gui/BufferQueue.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">wp&lt;ConsumerListener&gt; mConsumerListener;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> BufferQueue::ProxyConsumerListener::onFrameAvailable(</span><br><span class="line">        <span class="keyword">const</span> BufferItem&amp; item) &#123;</span><br><span class="line">    <span class="function">sp&lt;ConsumerListener&gt; <span class="title">listener</span><span class="params">(mConsumerListener.promote())</span></span>;</span><br><span class="line">    <span class="keyword">if</span> (listener != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        listener-&gt;onFrameAvailable(item);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>在ConsumerBase.h中该类继承ConsumerListener。所以此处的mConsumerListener是ConsumerBase中的实现。</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/gui/include/gui/ConsumerBase.h</span></figcaption><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="class"><span class="keyword">class</span> <span class="title">ConsumerBase</span> :</span> <span class="keyword">public</span> <span class="keyword">virtual</span> RefBase,</span><br><span class="line">        <span class="keyword">protected</span> ConsumerListener &#123;...&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/gui/ConsumerBase.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">void</span> <span class="title">ConsumerBase::onFrameAvailable</span><span class="params">(<span class="keyword">const</span> BufferItem&amp; item)</span> </span>&#123;</span><br><span class="line">    CB_LOGV(<span class="string">"onFrameAvailable"</span>);</span><br><span class="line"></span><br><span class="line">    sp&lt;FrameAvailableListener&gt; listener;</span><br><span class="line">    &#123; <span class="comment">// scope for the lock</span></span><br><span class="line">        <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mFrameAvailableMutex)</span></span>;</span><br><span class="line">        listener = mFrameAvailableListener.promote();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (listener != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        CB_LOGV(<span class="string">"actually calling onFrameAvailable"</span>);</span><br><span class="line">        listener-&gt;onFrameAvailable(item);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>在BufferQueueLayer中，<code>class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener{......}</code>。</li></ol><p>而<code>class BufferLayerConsumer : public ConsumerBase...</code></p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="function"><span class="keyword">void</span> <span class="title">BufferQueueLayer::onFrameAvailable</span><span class="params">(<span class="keyword">const</span> BufferItem&amp; item)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    <span class="comment">// Add this buffer from our internal queue tracker</span></span><br><span class="line">    &#123; <span class="comment">// Autolock scope</span></span><br><span class="line">        <span class="keyword">if</span> (mFlinger-&gt;mUseSmart90ForVideo) &#123;</span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">nsecs_t</span> presentTime = item.mIsAutoTimestamp ? <span class="number">0</span> : item.mTimestamp;</span><br><span class="line">            mFlinger-&gt;mScheduler-&gt;addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,</span><br><span class="line">                                                            item.mHdrMetadata.validTypes != <span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mQueueItemLock)</span></span>;</span><br><span class="line">        <span class="comment">// FrameNumber被重置时，同时重置mLastFrameNumberReceived</span></span><br><span class="line">        <span class="keyword">if</span> (item.mFrameNumber == <span class="number">1</span>) &#123;</span><br><span class="line">            mLastFrameNumberReceived = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//确保mFrameNumber的顺序</span></span><br><span class="line">        <span class="keyword">while</span> (item.mFrameNumber != mLastFrameNumberReceived + <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">status_t</span> result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(<span class="number">500</span>));</span><br><span class="line">            <span class="keyword">if</span> (result != NO_ERROR) &#123;</span><br><span class="line">                ALOGE(<span class="string">"[%s] Timed out waiting on callback"</span>, mName.<span class="built_in">string</span>());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//将新的buffer放入Layer队列中，同时对mQueuedFrames+1操作</span></span><br><span class="line">        mQueueItems.push_back(item);</span><br><span class="line">        mQueuedFrames++;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Wake up any pending callbacks</span></span><br><span class="line">        mLastFrameNumberReceived = item.mFrameNumber;</span><br><span class="line">        mQueueItemCondition.broadcast();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//buffer属性保存到mInterceptor中</span></span><br><span class="line">    mFlinger-&gt;mInterceptor-&gt;saveBufferUpdate(<span class="keyword">this</span>, item.mGraphicBuffer-&gt;getWidth(),</span><br><span class="line">                                             item.mGraphicBuffer-&gt;getHeight(), item.mFrameNumber);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (isRemovedFromCurrentState()) &#123;</span><br><span class="line">        fakeVsync();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//触发LayerUpdate</span></span><br><span class="line">        mFlinger-&gt;signalLayerUpdate();</span><br><span class="line">    &#125;</span><br><span class="line">    mConsumer-&gt;onBufferAvailable(item);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h3><ol><li>BufferQueueLayer有专门的Consumer，即BufferLayerConsumer。BufferLayerConsumer继承ConsumerBase。ConsumerBase通过IGraphicBufferConsumer和BufferQueue进行通信。  </li><li>BufferQueue中的frameAvailableListener，是一个IConsumerListener的接口，对应的Binder的Bn端实现为<code>ProxyConsumerListener</code>。</li><li>BufferQueueLayer实现了ContentsChangedListener，ContentsChangedListener继承FrameAvailableListener。BufferQueueLayer的Listener实现，被传给了ConsumerBase。  </li><li><code>ConsumerBase实现ConsumerListener接口，构建ConsumerBase时，会创建ProxyConsumerListener</code>，将ConsumerBase实现的Listener接口传给ProxyConsumerListener。</li><li>BufferQueue中Listener回调时，会回调到ConsumerBase中。ConsumerBase中再通过BufferQueueLayer实现的，传下来的Listener回调到BufferLayer中。</li></ol><p><img src="SF-%E6%B6%88%E8%B4%B9%E8%80%85onFrameAvailable.png" alt="消费者onFrameAvailable触发类图"></p><hr><h2 id="MessageQueue消息队列"><a href="#MessageQueue消息队列" class="headerlink" title="MessageQueue消息队列"></a>MessageQueue消息队列</h2><blockquote><p>Android的消息处理机制请参考：<a href="https://wizzie.top/Blog/2019/09/22/2019/190922-android-handler-cpp/#SurfaceFlinger%E7%9A%84%E6%B6%88%E6%81%AF%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6" target="_blank" rel="noopener">Android Handler消息循环处理机制(例ActivityThread)</a></p></blockquote><p>在SurfaceFlinger模块有单独的MessageQueue处理流程。在SF创建的时候，构造函数会创建<code>meventqueue</code>对象。在其onFirstRef函数中调用<code>mEventQueue-&gt;init(this);</code>进行初始化。</p><p>MessageQueue初始化时，创建了一个Looper和Handler。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/MessageQueue.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">void</span> <span class="title">MessageQueue::init</span><span class="params">(<span class="keyword">const</span> sp&lt;SurfaceFlinger&gt;&amp; flinger)</span> </span>&#123;</span><br><span class="line">    mFlinger = flinger;</span><br><span class="line">    mLooper = <span class="keyword">new</span> Looper(<span class="literal">true</span>);</span><br><span class="line">    mHandler = <span class="keyword">new</span> Handler(*<span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="setEventThread变更"><a href="#setEventThread变更" class="headerlink" title="setEventThread变更"></a>setEventThread变更</h3><p>在Android Q中，EventThread的<code>setEventThread</code>操作具体在<code>SurfaceFlinger::enableVSyncInjections</code>中进行。</p><p>而非之前的是在SurfaceFlinger的init初始化函数中。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//头文件</span></span><br><span class="line"><span class="comment">//mEventQueue是MessageQueue的一个栈对象</span></span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;MessageQueue&gt; mEventQueue;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">SurfaceFlinger::enableVSyncInjections</span><span class="params">(<span class="keyword">bool</span> enable)</span> </span>&#123;</span><br><span class="line">    postMessageSync(<span class="keyword">new</span> LambdaMessage([&amp;] &#123;</span><br><span class="line">        ......</span><br><span class="line"></span><br><span class="line">        <span class="comment">// TODO(b/128863962): Part of the Injector should be refactored, so that it</span></span><br><span class="line">        <span class="comment">// can be passed to Scheduler.</span></span><br><span class="line">        <span class="keyword">if</span> (enable) &#123;</span><br><span class="line">            ALOGV(<span class="string">"VSync Injections enabled"</span>);</span><br><span class="line">            <span class="keyword">if</span> (mVSyncInjector.get() == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">                mVSyncInjector = <span class="built_in">std</span>::make_unique&lt;InjectVSyncSource&gt;();</span><br><span class="line">                mInjectorEventThread = <span class="built_in">std</span>::make_unique&lt;</span><br><span class="line">                        impl::EventThread&gt;(mVSyncInjector.get(),</span><br><span class="line">                                           impl::EventThread::InterceptVSyncsCallback(),</span><br><span class="line">                                           <span class="string">"injEventThread"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            mEventQueue-&gt;setEventThread(mInjectorEventThread.get(), <span class="built_in">std</span>::move(resyncCallback));</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            ALOGV(<span class="string">"VSync Injections disabled"</span>);</span><br><span class="line">            mEventQueue-&gt;setEventThread(mScheduler-&gt;getEventThread(mSfConnectionHandle),</span><br><span class="line">                                        <span class="built_in">std</span>::move(resyncCallback));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        mInjectVSyncs = enable;</span><br><span class="line">    &#125;));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>setEventThread函数做了两件事：</p><ol><li>创建一个BitTube对象mEventTube</li><li>创建一个EventConnection</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/MessageQueue.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">//头文件，是BitTube对象</span></span><br><span class="line">gui::BitTube mEventTube;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">MessageQueue::setEventThread</span><span class="params">(android::EventThread* eventThread,</span></span></span><br><span class="line"><span class="function"><span class="params">                                  ResyncCallback resyncCallback)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mEventThread == eventThread) &#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">if</span> (mEventTube.getFd() &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">        mLooper-&gt;removeFd(mEventTube.getFd());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mEventThread = eventThread;</span><br><span class="line">    mEvents = eventThread-&gt;createEventConnection(<span class="built_in">std</span>::move(resyncCallback));</span><br><span class="line">    <span class="comment">//将mEventTube和EventConnection关联</span></span><br><span class="line">    mEvents-&gt;stealReceiveChannel(&amp;mEventTube);</span><br><span class="line">    <span class="comment">//将fd添加到MessageQueue的Looper中</span></span><br><span class="line">    <span class="comment">//Looper的callback（入参）为MessageQueue::cb_eventReceiver</span></span><br><span class="line">    <span class="comment">//一旦有数据到来就会调用cb_eventReceiver</span></span><br><span class="line">    mLooper-&gt;addFd(mEventTube.getFd(), <span class="number">0</span>, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,</span><br><span class="line">                   <span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//data为MessageQueue本身有数据到来就会调用，作用是负责处理EventThread发送过来的信号</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">MessageQueue::cb_eventReceiver</span><span class="params">(<span class="keyword">int</span> fd, <span class="keyword">int</span> events, <span class="keyword">void</span>* data)</span> </span>&#123;</span><br><span class="line">    MessageQueue* <span class="built_in">queue</span> = <span class="keyword">reinterpret_cast</span>&lt;MessageQueue*&gt;(data);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">queue</span>-&gt;eventReceiver(fd, events);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">//创建connection</span></span><br><span class="line"><span class="function">sp&lt;EventThreadConnection&gt; <span class="title">EventThread::createEventConnection</span><span class="params">(ResyncCallback resyncCallback)</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> EventThreadConnection(<span class="keyword">const_cast</span>&lt;EventThread*&gt;(<span class="keyword">this</span>), <span class="built_in">std</span>::move(resyncCallback));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Connection创建的时候先调用OnFirstRef进行注册</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EventThreadConnection::onFirstRef</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// <span class="doctag">NOTE:</span> mEventThread doesn't hold a strong reference on us</span></span><br><span class="line">    mEventThread-&gt;registerDisplayEventConnection(<span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//注册，将Connection将会被添加到mDisplayEventConnections 中</span></span><br><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">EventThread::registerDisplayEventConnection</span><span class="params">(<span class="keyword">const</span> sp&lt;EventThreadConnection&gt;&amp; connection)</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// this should never happen</span></span><br><span class="line">    <span class="keyword">auto</span> it = <span class="built_in">std</span>::find(mDisplayEventConnections.cbegin(),</span><br><span class="line">            mDisplayEventConnections.cend(), connection);</span><br><span class="line">    <span class="keyword">if</span> (it != mDisplayEventConnections.cend()) &#123;</span><br><span class="line">        ALOGW(<span class="string">"DisplayEventConnection %p already exists"</span>, connection.get());</span><br><span class="line">        mCondition.notify_all();</span><br><span class="line">        <span class="keyword">return</span> ALREADY_EXISTS;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mDisplayEventConnections.push_back(connection); <span class="comment">//添加</span></span><br><span class="line">    mCondition.notify_all();</span><br><span class="line">    <span class="keyword">return</span> NO_ERROR;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建BitTube对象，构造函数会调用init函数：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/gui/BitTube.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">static</span> <span class="keyword">const</span> <span class="keyword">size_t</span> DEFAULT_SOCKET_BUFFER_SIZE = <span class="number">4</span> * <span class="number">1024</span>;</span><br><span class="line"></span><br><span class="line">BitTube::BitTube(<span class="keyword">size_t</span> bufsize) &#123;</span><br><span class="line">    init(bufsize, bufsize);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//默认创建一个4k的BitTube，BitTube封装的是一对socket，一个发送，一个接收，可传输的Buffer大小为4K</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">BitTube::init</span><span class="params">(<span class="keyword">size_t</span> rcvbuf, <span class="keyword">size_t</span> sndbuf)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> sockets[<span class="number">2</span>];</span><br><span class="line">    <span class="keyword">if</span> (socketpair(AF_UNIX, SOCK_SEQPACKET, <span class="number">0</span>, sockets) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">size_t</span> size = DEFAULT_SOCKET_BUFFER_SIZE;</span><br><span class="line">        setsockopt(sockets[<span class="number">0</span>], SOL_SOCKET, SO_RCVBUF, &amp;rcvbuf, <span class="keyword">sizeof</span>(rcvbuf));</span><br><span class="line">        setsockopt(sockets[<span class="number">1</span>], SOL_SOCKET, SO_SNDBUF, &amp;sndbuf, <span class="keyword">sizeof</span>(sndbuf));</span><br><span class="line">        <span class="comment">// since we don't use the "return channel", we keep it small...</span></span><br><span class="line">        setsockopt(sockets[<span class="number">0</span>], SOL_SOCKET, SO_SNDBUF, &amp;size, <span class="keyword">sizeof</span>(size));</span><br><span class="line">        setsockopt(sockets[<span class="number">1</span>], SOL_SOCKET, SO_RCVBUF, &amp;size, <span class="keyword">sizeof</span>(size));</span><br><span class="line">        fcntl(sockets[<span class="number">0</span>], F_SETFL, O_NONBLOCK);</span><br><span class="line">        fcntl(sockets[<span class="number">1</span>], F_SETFL, O_NONBLOCK);</span><br><span class="line">        mReceiveFd.reset(sockets[<span class="number">0</span>]);</span><br><span class="line">        mSendFd.reset(sockets[<span class="number">1</span>]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        mReceiveFd.reset();</span><br><span class="line">        ALOGE(<span class="string">"BitTube: pipe creation failed (%s)"</span>, strerror(errno));</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">int</span> <span class="title">BitTube::getFd</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> mReceiveFd;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="signalLayerUpdate通知Layer更新信息"><a href="#signalLayerUpdate通知Layer更新信息" class="headerlink" title="signalLayerUpdate通知Layer更新信息"></a>signalLayerUpdate通知Layer更新信息</h2><p>接着上面第一部分的流程，触发SF的合成从signalLayerUpdate开始。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><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">void</span> <span class="title">SurfaceFlinger::signalLayerUpdate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mScheduler-&gt;resetIdleTimer();</span><br><span class="line">    mEventQueue-&gt;invalidate();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用到MessageQueue的invalidate函数。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/MessageQueue.cpp</span></figcaption><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">void</span> <span class="title">MessageQueue::invalidate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mEvents-&gt;requestNextVsync();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>请求下一个Vsync（垂直同步机制），主要作用是通知Vsync机制在下一个SF的Vsync到来的时候唤醒SF进行工作，从而进行合成处理。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">EventThreadConnection::requestNextVsync</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ATRACE_NAME(<span class="string">"requestNextVsync"</span>);</span><br><span class="line">    mEventThread-&gt;requestNextVsync(<span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EventThread::requestNextVsync</span><span class="params">(<span class="keyword">const</span> sp&lt;EventThreadConnection&gt;&amp; connection)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (connection-&gt;resyncCallback) &#123;</span><br><span class="line">        connection-&gt;resyncCallback();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line">    <span class="comment">//如果为空，则赋值，并且触发notify</span></span><br><span class="line">    <span class="keyword">if</span> (connection-&gt;vsyncRequest == VSyncRequest::None) &#123;</span><br><span class="line">        connection-&gt;vsyncRequest = VSyncRequest::Single;</span><br><span class="line">        mCondition.notify_all();</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="keyword">enum</span> <span class="class"><span class="keyword">class</span> <span class="title">VSyncRequest</span> &#123;</span></span><br><span class="line">    None = <span class="number">-1</span>,</span><br><span class="line">    Single = <span class="number">0</span>,</span><br><span class="line">    Periodic = <span class="number">1</span>,</span><br><span class="line">    <span class="comment">// Subsequent values are periods.</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>然后会触发threadmain函数，这个和Android P上的threadloop流程是不同的。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EventThread::threadMain</span><span class="params">(<span class="built_in">std</span>::unique_lock&lt;<span class="built_in">std</span>::mutex&gt;&amp; lock)</span> </span>&#123;</span><br><span class="line">    DisplayEventConsumers consumers;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (mState != State::Quit) &#123;</span><br><span class="line">        <span class="built_in">std</span>::optional&lt;DisplayEventReceiver::Event&gt; event;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Determine next event to dispatch.</span></span><br><span class="line">        <span class="keyword">if</span> (!mPendingEvents.empty()) &#123;</span><br><span class="line">            event = mPendingEvents.front();</span><br><span class="line">            mPendingEvents.pop_front();</span><br><span class="line">            <span class="comment">//event处理两种事件：</span></span><br><span class="line">            <span class="comment">//1. 热插拔事件</span></span><br><span class="line">            <span class="comment">//2. vsync事件</span></span><br><span class="line">            <span class="keyword">switch</span> (event-&gt;header.type) &#123;</span><br><span class="line">                <span class="keyword">case</span> DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:</span><br><span class="line">                    <span class="keyword">if</span> (event-&gt;hotplug.connected &amp;&amp; !mVSyncState) &#123;</span><br><span class="line">                        mVSyncState.emplace(event-&gt;header.displayId);</span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!event-&gt;hotplug.connected &amp;&amp; mVSyncState &amp;&amp;</span><br><span class="line">                               mVSyncState-&gt;displayId == event-&gt;header.displayId) &#123;</span><br><span class="line">                        mVSyncState.reset();</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> DisplayEventReceiver::DISPLAY_EVENT_VSYNC:</span><br><span class="line">                    <span class="keyword">if</span> (mInterceptVSyncsCallback) &#123;</span><br><span class="line">                        <span class="comment">//有vsync事件要分发</span></span><br><span class="line">                        mInterceptVSyncsCallback(event-&gt;header.timestamp);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">bool</span> vsyncRequested = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Find connections that should consume this event.</span></span><br><span class="line">        <span class="keyword">auto</span> it = mDisplayEventConnections.begin();</span><br><span class="line">        <span class="keyword">while</span> (it != mDisplayEventConnections.end()) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">const</span> <span class="keyword">auto</span> connection = it-&gt;promote()) &#123;</span><br><span class="line">                vsyncRequested |= connection-&gt;vsyncRequest != VSyncRequest::None;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (event &amp;&amp; shouldConsumeEvent(*event, connection)) &#123;</span><br><span class="line">                    consumers.push_back(connection);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                ++it;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                it = mDisplayEventConnections.erase(it);</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> (!consumers.empty()) &#123;</span><br><span class="line">            <span class="comment">//分发事件</span></span><br><span class="line">            dispatchEvent(*event, consumers);</span><br><span class="line">            consumers.clear();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        State nextState;</span><br><span class="line">        <span class="keyword">if</span> (mVSyncState &amp;&amp; vsyncRequested) &#123;</span><br><span class="line">            nextState = mVSyncState-&gt;synthetic ? State::SyntheticVSync : State::VSync;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            ALOGW_IF(!mVSyncState, <span class="string">"Ignoring VSYNC request while display is disconnected"</span>);</span><br><span class="line">            nextState = State::Idle;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mState != nextState) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mState == State::VSync) &#123;</span><br><span class="line">                mVSyncSource-&gt;setVSyncEnabled(<span class="literal">false</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nextState == State::VSync) &#123;</span><br><span class="line">                mVSyncSource-&gt;setVSyncEnabled(<span class="literal">true</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            mState = nextState;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (event) &#123;</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">// Wait for event or client registration/request.</span></span><br><span class="line">        <span class="keyword">if</span> (mState == State::Idle) &#123;</span><br><span class="line">            mCondition.wait(lock);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// Generate a fake VSYNC after a long timeout in case the driver stalls. When the</span></span><br><span class="line">            <span class="comment">// display is off, keep feeding clients at 60 Hz.</span></span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">auto</span> timeout = mState == State::SyntheticVSync ? <span class="number">16</span>ms : <span class="number">1000</span>ms;</span><br><span class="line">            <span class="keyword">if</span> (mCondition.wait_for(lock, timeout) == <span class="built_in">std</span>::cv_status::timeout) &#123;</span><br><span class="line">                ALOGW_IF(mState == State::VSync, <span class="string">"Faking VSYNC due to driver stall"</span>);</span><br><span class="line"></span><br><span class="line">                LOG_FATAL_IF(!mVSyncState);</span><br><span class="line">                mPendingEvents.push_back(makeVSync(mVSyncState-&gt;displayId,</span><br><span class="line">                                                   systemTime(SYSTEM_TIME_MONOTONIC),</span><br><span class="line">                                                   ++mVSyncState-&gt;count));</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">//头文件</span></span><br><span class="line">    <span class="comment">// State machine for event loop.</span></span><br><span class="line">    <span class="keyword">enum</span> <span class="class"><span class="keyword">class</span> <span class="title">State</span> &#123;</span></span><br><span class="line">        Idle,</span><br><span class="line">        Quit,</span><br><span class="line">        SyntheticVSync,</span><br><span class="line">        VSync,</span><br><span class="line">    &#125;;</span><br></pre></td></tr></table></figure><h3 id="VSYNC事件到来"><a href="#VSYNC事件到来" class="headerlink" title="VSYNC事件到来"></a>VSYNC事件到来</h3><p>将会回调onVSyncEvent：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">void</span> <span class="title">EventThread::onVSyncEvent</span><span class="params">(<span class="keyword">nsecs_t</span> timestamp)</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">    LOG_FATAL_IF(!mVSyncState);</span><br><span class="line">    mPendingEvents.push_back(makeVSync(mVSyncState-&gt;displayId, timestamp, ++mVSyncState-&gt;count));</span><br><span class="line">    mCondition.notify_all();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Hotplug事件到来"><a href="#Hotplug事件到来" class="headerlink" title="Hotplug事件到来"></a>Hotplug事件到来</h3><p>将会回调onHotplugReceived：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="function"><span class="keyword">void</span> <span class="title">EventThread::onHotplugReceived</span><span class="params">(PhysicalDisplayId displayId, <span class="keyword">bool</span> connected)</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="built_in">std</span>::lock_guard&lt;<span class="built_in">std</span>::mutex&gt; <span class="title">lock</span><span class="params">(mMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">    mPendingEvents.push_back(makeHotplug(displayId, systemTime(), connected));</span><br><span class="line">    mCondition.notify_all();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="dispatchEvent分发事件"><a href="#dispatchEvent分发事件" class="headerlink" title="dispatchEvent分发事件"></a>dispatchEvent分发事件</h3><p>从上面的threadMain函数会调用dispatchEvent函数分发事件：</p><p>Connection通过postEvent将Event抛出来后，通过sendEvents将事件发出去。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">EventThread::dispatchEvent</span><span class="params">(<span class="keyword">const</span> DisplayEventReceiver::Event&amp; event,</span></span></span><br><span class="line"><span class="function"><span class="params">                                <span class="keyword">const</span> DisplayEventConsumers&amp; consumers)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; consumer : consumers) &#123;</span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="keyword">switch</span> (consumer-&gt;postEvent(event)) &#123;</span><br><span class="line">            <span class="keyword">case</span> NO_ERROR:</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> -EAGAIN:</span><br><span class="line">                <span class="comment">// <span class="doctag">TODO:</span> Try again if pipe is full.</span></span><br><span class="line">                ALOGW(<span class="string">"Failed dispatching %s for %s"</span>, toString(event).c_str(),</span><br><span class="line">                      toString(*consumer).c_str());</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="comment">// Treat EPIPE and other errors as fatal.</span></span><br><span class="line">                removeDisplayEventConnectionLocked(consumer);</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="function"><span class="keyword">status_t</span> <span class="title">EventThreadConnection::postEvent</span><span class="params">(<span class="keyword">const</span> DisplayEventReceiver::Event&amp; event)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="keyword">ssize_t</span> size = DisplayEventReceiver::sendEvents(&amp;mChannel, &amp;event, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> size &lt; <span class="number">0</span> ? <span class="keyword">status_t</span>(size) : <span class="keyword">status_t</span>(NO_ERROR);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>DisplayEventReceiver中是通过BitTube将事件发出去，sendObjects注意这里的参数:</p><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/gui/DisplayEventReceiver.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">ssize_t</span> <span class="title">DisplayEventReceiver::sendEvents</span><span class="params">(gui::BitTube* dataChannel,</span></span></span><br><span class="line"><span class="function"><span class="params">        Event <span class="keyword">const</span>* events, <span class="keyword">size_t</span> count)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> gui::BitTube::sendObjects(dataChannel, events, count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="MessageQueue处理Message"><a href="#MessageQueue处理Message" class="headerlink" title="MessageQueue处理Message"></a>MessageQueue处理Message</h3><p>SF的线程run时，有一个死循环，循环等待事件：</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        waitForEvent();</span><br><span class="line">    &#125; <span class="keyword">while</span> (<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::waitForEvent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mEventQueue-&gt;waitMessage();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在MessageQueue中，会有looper一直循环等待消息。</p><p>waitMessage，通过采用一个死循环，处理Looper的pollOnce。Looper内部的主要是采用epoll_wait对fd进行监听，BitTube发送Event对象后，epoll_wait结束，调用callback，处理事件。</p><p>可参考Handler文章：<a href="https://wizzie.top/Blog/2019/09/22/2019/190922-android-handler-cpp/#%E6%B6%88%E6%81%AF%E5%8F%91%E9%80%81" target="_blank" rel="noopener">Android Handler消息循环处理机制</a></p><figure class="highlight cpp"><figcaption><span>MessageQueue.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">MessageQueue::waitMessage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        IPCThreadState::self()-&gt;flushCommands();</span><br><span class="line">        <span class="keyword">int32_t</span> ret = mLooper-&gt;pollOnce(<span class="number">-1</span>);</span><br><span class="line">        <span class="keyword">switch</span> (ret) &#123;</span><br><span class="line">            <span class="keyword">case</span> Looper::POLL_WAKE:</span><br><span class="line">            <span class="keyword">case</span> Looper::POLL_CALLBACK:</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">case</span> Looper::POLL_ERROR:</span><br><span class="line">                ALOGE(<span class="string">"Looper::POLL_ERROR"</span>);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">case</span> Looper::POLL_TIMEOUT:</span><br><span class="line">                <span class="comment">// timeout (should not happen)</span></span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="comment">// should not happen</span></span><br><span class="line">                ALOGE(<span class="string">"Looper::pollOnce() returned unknown status %d"</span>, ret);</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> (<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>MessageQueue对应的callback为cb_eventReceiver：</p><figure class="highlight cpp"><figcaption><span>MessageQueue.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="keyword">enum</span> &#123;</span><br><span class="line">        INVALIDATE = <span class="number">0</span>,</span><br><span class="line">        REFRESH = <span class="number">1</span>,</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">MessageQueue::cb_eventReceiver</span><span class="params">(<span class="keyword">int</span> fd, <span class="keyword">int</span> events, <span class="keyword">void</span>* data)</span> </span>&#123;</span><br><span class="line">    MessageQueue* <span class="built_in">queue</span> = <span class="keyword">reinterpret_cast</span>&lt;MessageQueue*&gt;(data);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">queue</span>-&gt;eventReceiver(fd, events);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">MessageQueue::eventReceiver</span><span class="params">(<span class="keyword">int</span> <span class="comment">/*fd*/</span>, <span class="keyword">int</span> <span class="comment">/*events*/</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">ssize_t</span> n;</span><br><span class="line">    DisplayEventReceiver::Event buffer[<span class="number">8</span>];</span><br><span class="line">    <span class="keyword">while</span> ((n = DisplayEventReceiver::getEvents(&amp;mEventTube, buffer, <span class="number">8</span>)) &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; n; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) &#123;</span><br><span class="line">                mHandler-&gt;dispatchInvalidate();</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">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">void</span> MessageQueue::Handler::dispatchInvalidate() &#123;</span><br><span class="line">    <span class="keyword">if</span> ((android_atomic_or(eventMaskInvalidate, &amp;mEventMask) &amp; eventMaskInvalidate) == <span class="number">0</span>) &#123;</span><br><span class="line">        mQueue.mLooper-&gt;sendMessage(<span class="keyword">this</span>, Message(MessageQueue::INVALIDATE));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>sendMessage</code>在Handler.java中调用到MessageQueue.java的<code>enqueueMessage</code>函数，然后调用JNI函数nativeWake，唤醒Looper.java的loop函数。</p><p>调用其中的dispatchMessage处理消息。</p><p>dispatchMessage函数会调用handleMessage函数，开始处理消息。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> MessageQueue::Handler::handleMessage(<span class="keyword">const</span> Message&amp; message) &#123;</span><br><span class="line">    <span class="keyword">switch</span> (message.what) &#123;</span><br><span class="line">        <span class="keyword">case</span> INVALIDATE:</span><br><span class="line">            android_atomic_and(~eventMaskInvalidate, &amp;mEventMask);</span><br><span class="line">            mQueue.mFlinger-&gt;onMessageReceived(message.what);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> REFRESH:</span><br><span class="line">            android_atomic_and(~eventMaskRefresh, &amp;mEventMask);</span><br><span class="line">            mQueue.mFlinger-&gt;onMessageReceived(message.what);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="onMessageReceived处理INVALIDATE消息"><a href="#onMessageReceived处理INVALIDATE消息" class="headerlink" title="onMessageReceived处理INVALIDATE消息"></a>onMessageReceived处理INVALIDATE消息</h2><p>MessageQueue触发Handler消息处理机制，开始触发INVALIDATE消息合成。</p><p><strong>大体分成两部分：</strong></p><ol><li>SF更新合成相关的信息（即handleMessageTransaction）</li><li>SF执行合成相关的操作并显示</li></ol><figure class="highlight cpp"><figcaption><span>SurfaceFlinger.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::onMessageReceived</span><span class="params">(<span class="keyword">int32_t</span> what)</span> NO_THREAD_SAFETY_ANALYSIS </span>&#123;</span><br><span class="line">    <span class="comment">//systrace抓取</span></span><br><span class="line">    ATRACE_CALL();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">switch</span> (what) &#123;</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::INVALIDATE: &#123;</span><br><span class="line">            <span class="comment">//丢帧处理</span></span><br><span class="line">            <span class="keyword">bool</span> frameMissed = previousFrameMissed();</span><br><span class="line">            <span class="keyword">bool</span> hwcFrameMissed = mHadDeviceComposition &amp;&amp; frameMissed;</span><br><span class="line">            <span class="keyword">bool</span> gpuFrameMissed = mHadClientComposition &amp;&amp; frameMissed;</span><br><span class="line">            ATRACE_INT(<span class="string">"FrameMissed"</span>, <span class="keyword">static_cast</span>&lt;<span class="keyword">int</span>&gt;(frameMissed));</span><br><span class="line">            ATRACE_INT(<span class="string">"HwcFrameMissed"</span>, <span class="keyword">static_cast</span>&lt;<span class="keyword">int</span>&gt;(hwcFrameMissed));</span><br><span class="line">            ATRACE_INT(<span class="string">"GpuFrameMissed"</span>, <span class="keyword">static_cast</span>&lt;<span class="keyword">int</span>&gt;(gpuFrameMissed));</span><br><span class="line">            <span class="keyword">if</span> (frameMissed) &#123;</span><br><span class="line">                mFrameMissedCount++;</span><br><span class="line">                mTimeStats-&gt;incrementMissedFrames();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (hwcFrameMissed) &#123;</span><br><span class="line">                mHwcFrameMissedCount++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (gpuFrameMissed) &#123;</span><br><span class="line">                mGpuFrameMissedCount++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (mUseSmart90ForVideo) &#123;</span><br><span class="line">                <span class="comment">// This call is made each time SF wakes up and creates a new frame. It is part</span></span><br><span class="line">                <span class="comment">// of video detection feature.</span></span><br><span class="line">                mScheduler-&gt;updateFpsBasedOnContent();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (performSetActiveConfig()) &#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> (frameMissed &amp;&amp; mPropagateBackpressure) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((hwcFrameMissed &amp;&amp; !gpuFrameMissed) ||</span><br><span class="line">                    mPropagateBackpressureClientComposition) &#123;</span><br><span class="line">                    signalLayerUpdate();</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//更新VR</span></span><br><span class="line">            updateVrFlinger();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//处理Transition，在合成前更新Layer的信息</span></span><br><span class="line">            <span class="keyword">bool</span> refreshNeeded = handleMessageTransaction();</span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            refreshNeeded |= handleMessageInvalidate();</span><br><span class="line"></span><br><span class="line">            updateCursorAsync();</span><br><span class="line">            updateInputFlinger();</span><br><span class="line"></span><br><span class="line">            refreshNeeded |= mRepaintEverything;</span><br><span class="line">            <span class="keyword">if</span> (refreshNeeded &amp;&amp; CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) &#123;</span><br><span class="line">                <span class="comment">//如果Transaction事务修改了窗口状态，则发出刷新信号</span></span><br><span class="line">                <span class="comment">//一个新的buffer将被请求</span></span><br><span class="line">                signalRefresh();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::REFRESH: &#123;</span><br><span class="line">            handleMessageRefresh();</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="一、丢帧处理frameMissed"><a href="#一、丢帧处理frameMissed" class="headerlink" title="一、丢帧处理frameMissed"></a>一、丢帧处理frameMissed</h3><p>如果丢帧，则<code>mPropagateBackpressure</code>为true。</p><p>该变量是在SurfaceFlinger的构造函数中，由<code>debug.sf.disable_backpressure</code>属性控制。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">property_get(<span class="string">"debug.sf.disable_backpressure"</span>, value, <span class="string">"0"</span>);</span><br><span class="line"> mPropagateBackpressure = !atoi(value);</span><br><span class="line"> ALOGI_IF(!mPropagateBackpressure, <span class="string">"Disabling backpressure propagation"</span>);</span><br><span class="line"></span><br><span class="line"> property_get(<span class="string">"debug.sf.enable_gl_backpressure"</span>, value, <span class="string">"0"</span>);</span><br><span class="line"> mPropagateBackpressureClientComposition = atoi(value);</span><br><span class="line"> ALOGI_IF(mPropagateBackpressureClientComposition,</span><br><span class="line">          <span class="string">"Enabling backpressure propagation for Client Composition"</span>);</span><br></pre></td></tr></table></figure><p>例如我的一加六 Android Q设备：</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 getprop debug.sf.disable_backpressure</span><br><span class="line">1</span><br></pre></td></tr></table></figure><h3 id="二、handleMessageTransaction处理Transaction"><a href="#二、handleMessageTransaction处理Transaction" class="headerlink" title="二、handleMessageTransaction处理Transaction"></a>二、handleMessageTransaction处理Transaction</h3><p>Vsync到来后，触发INVALIDATE消息时会先处理Transition。</p><p>如上代码先调用<code>handleMessageTransaction</code>。这个过程就是处理应用传过来的各种Transaction。</p><p><strong>大致函数调用流程：</strong></p><p><strong>handleMessageTransaction -&gt; handleTransaction -&gt; <code>handleTransactionLocked</code> -&gt; (processDisplayChangesLocked) -&gt; commitTransaction()</strong></p><p>handleMessageTransaction主要处理Layer属性变化，显示设备变化等情况，最终将变化的信息mCurrentState提交到mDrawingState, 等待合成处理.</p><p>即最终<code>commitTransaction</code>函数会有个状态更替，将mCurrentState赋值给了mDrawingState。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">void</span> <span class="title">SurfaceFlinger::commitTransaction</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    .....</span><br><span class="line">     withTracingLock([&amp;]() &#123;</span><br><span class="line">        mDrawingState = mCurrentState;</span><br><span class="line">        <span class="comment">// clear the "changed" flags in current state</span></span><br><span class="line">        mCurrentState.colorMatrixChanged = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        mDrawingState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">            layer-&gt;commitChildList();</span><br><span class="line">            <span class="keyword">if</span> (mOffscreenLayers.count(layer)) &#123;</span><br><span class="line">                mOffscreenLayers.erase(layer);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        commitOffscreenLayers();</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    mTransactionPending = <span class="literal">false</span>;</span><br><span class="line">    mAnimTransactionPending = <span class="literal">false</span>;</span><br><span class="line">    mTransactionCV.broadcast();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="mCurrentState和mDrawingState"><a href="#mCurrentState和mDrawingState" class="headerlink" title="mCurrentState和mDrawingState"></a>mCurrentState和mDrawingState</h4><blockquote><p>可参考：<a href="https://wizzie.top/Blog/2019/12/22/2019/191222_android_HWC2/#mCurrentState%E5%92%8CmDrawingState" target="_blank" rel="noopener">Android SurfaceFlinger和HWC2概述 - mCurrentState和mDrawingState</a></p></blockquote><ul><li>mCurrentState状态：准备数据，应用传过来的数据保存在mCurrentState中。</li><li>mDrawingState状态：进程合成状态，需要进行合成的数据保存在mDrawingState中。</li></ul><p>即每次合成时，先更新一下状态数据。每一层Layer也需要去更新状态数据。</p><h3 id="handleTransactionLocked"><a href="#handleTransactionLocked" class="headerlink" title="handleTransactionLocked"></a>handleTransactionLocked</h3><h4 id="1-检查mCurrentState的Layer的可见区域"><a href="#1-检查mCurrentState的Layer的可见区域" class="headerlink" title="1. 检查mCurrentState的Layer的可见区域"></a>1. 检查mCurrentState的Layer的可见区域</h4><p><strong>该函数第一部分就是遍历mCurrentState的Layer，并检查Layer的可见区域VisableRegion是否发生变化。</strong></p><ul><li>如果没发生变化则不需要检查layer</li><li>如果发生变化，则对比该Layer的显示区域和原来的显示区域是否发生变化，若变化则设置mVisibleRegionsDirty为true</li></ul><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::handleTransactionLocked</span><span class="params">(<span class="keyword">uint32_t</span> transactionFlags)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//第一部分</span></span><br><span class="line">    <span class="comment">// Notify all layers of available frames</span></span><br><span class="line">    mCurrentState.traverseInZOrder([](Layer* layer) &#123;</span><br><span class="line">        layer-&gt;notifyAvailableFrames();</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//遍历mCurrentState中的所有的layer</span></span><br><span class="line">    <span class="keyword">if</span> ((transactionFlags &amp; eTraversalNeeded) || mTraversalNeededMainThread) &#123;</span><br><span class="line">        mCurrentState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">            <span class="comment">//根据eTransactionNeeded判断Layer是否发生变化（如果Layer发生变化则会设置这个flag）</span></span><br><span class="line">            <span class="keyword">uint32_t</span> trFlags = layer-&gt;getTransactionFlags(eTransactionNeeded);</span><br><span class="line">            <span class="keyword">if</span> (!trFlags) <span class="keyword">return</span>;</span><br><span class="line">            <span class="comment">//对发生变化的layer执行doTransaction函数，对比Layer旧的状态和新的状态是否发生变化</span></span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">uint32_t</span> flags = layer-&gt;doTransaction(<span class="number">0</span>);</span><br><span class="line">            <span class="comment">//判断Layer的可见区域是否发生变化</span></span><br><span class="line">            <span class="comment">//当Layer的可见区域大小发生变化之后，设置mVisibleRegionsDirty为true</span></span><br><span class="line">            <span class="keyword">if</span> (flags &amp; Layer::eVisibleRegion)</span><br><span class="line">                mVisibleRegionsDirty = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (flags &amp; Layer::eInputInfoChanged) &#123;</span><br><span class="line">                mInputInfoChanged = <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        mTraversalNeededMainThread = <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//第二部分</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h4 id="2-检查显示设备是否变化"><a href="#2-检查显示设备是否变化" class="headerlink" title="2. 检查显示设备是否变化"></a>2. 检查显示设备是否变化</h4><p><strong>该函数第二部分先调用<code>processDisplayChangesLocked</code>函数。</strong></p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::handleTransactionLocked</span><span class="params">(<span class="keyword">uint32_t</span> transactionFlags)</span></span></span><br><span class="line"><span class="function"></span>&#123;</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">//如果需要，执行显示自己的事务</span></span><br><span class="line">    <span class="comment">//遍历并检查所有的显示设备，检查显示设备是否发生了增加或者减少.并做相应的处理</span></span><br><span class="line">    <span class="keyword">if</span> (transactionFlags &amp; eDisplayTransactionNeeded) &#123;</span><br><span class="line">        processDisplayChangesLocked();</span><br><span class="line">        processDisplayHotplugEventsLocked();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (transactionFlags &amp; (eDisplayLayerStackChanged|eDisplayTransactionNeeded)) &#123;</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">void</span> <span class="title">SurfaceFlinger::processDisplayChangesLocked</span><span class="params">()</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">    const KeyedVector&lt;wp&lt;IBinder&gt;, DisplayDeviceState&gt;&amp; curr(mCurrentState.displays);</span><br><span class="line">    <span class="comment">//上次合成时的显示设备的信息（上次合成的mDrawingState信息，本地还未开始正式合成操作）</span></span><br><span class="line">    const KeyedVector&lt;wp&lt;IBinder&gt;, DisplayDeviceState&gt;&amp; draw(mDrawingState.displays);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//判断当前和上次的设备信息是否发生变化</span></span><br><span class="line">    <span class="keyword">if</span> (!curr.isIdenticalTo(draw)) &#123;</span><br><span class="line">        mVisibleRegionsDirty = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">size_t</span> cc = curr.size();</span><br><span class="line">        <span class="keyword">size_t</span> dc = draw.size();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//找到被删除的显示设备(在draw状态而不是current状态的列表中，说明有设备删除)</span></span><br><span class="line">        <span class="comment">//处理发生变化的设备（两个列表都存在该设备）</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; dc;) &#123;</span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">ssize_t</span> j = curr.indexOfKey(draw.keyAt(i));</span><br><span class="line">            <span class="keyword">if</span> (j &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// 1. 删除显示设备：in drawing state but not in current state</span></span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">const</span> <span class="keyword">auto</span> display = getDisplayDeviceLocked(draw.keyAt(i))) &#123;</span><br><span class="line">                    <span class="comment">// 断开前保存设备ID（Save display ID before disconnecting.）</span></span><br><span class="line">                    <span class="keyword">const</span> <span class="keyword">auto</span> displayId = display-&gt;getId();</span><br><span class="line">                    display-&gt;disconnect();</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (!display-&gt;isVirtual()) &#123;</span><br><span class="line">                        LOG_ALWAYS_FATAL_IF(!displayId);</span><br><span class="line">                        dispatchDisplayHotplugEvent(displayId-&gt;value, <span class="literal">false</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                mDisplays.erase(draw.keyAt(i));</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">//设备在两个列表都存在，但是有信息发生变化</span></span><br><span class="line">                <span class="keyword">const</span> DisplayDeviceState&amp; state(curr[j]);</span><br><span class="line">                <span class="keyword">const</span> wp&lt;IBinder&gt;&amp; displayToken = curr.keyAt(j);</span><br><span class="line">                <span class="keyword">const</span> sp&lt;IBinder&gt; state_binder = IInterface::asBinder(state.surface);</span><br><span class="line">                <span class="keyword">const</span> sp&lt;IBinder&gt; draw_binder = IInterface::asBinder(draw[i].surface);</span><br><span class="line">                <span class="keyword">if</span> (state_binder != draw_binder) &#123;</span><br><span class="line">                    <span class="comment">// changing the surface is like destroying and</span></span><br><span class="line">                    <span class="comment">// recreating the DisplayDevice, so we just remove it</span></span><br><span class="line">                    <span class="comment">// from the drawing state, so that it get re-added</span></span><br><span class="line">                    <span class="comment">// below.</span></span><br><span class="line">                    <span class="keyword">if</span> (<span class="keyword">const</span> <span class="keyword">auto</span> display = getDisplayDeviceLocked(displayToken)) &#123;</span><br><span class="line">                        display-&gt;disconnect();</span><br><span class="line">                    &#125;</span><br><span class="line">                    mDisplays.erase(displayToken);</span><br><span class="line">                    mDrawingState.displays.removeItemsAt(i);</span><br><span class="line">                    dc--;</span><br><span class="line">                    <span class="comment">// at this point we must loop to the next item</span></span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//更新显示设备信息</span></span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">const</span> <span class="keyword">auto</span> display = getDisplayDeviceLocked(displayToken)) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (state.layerStack != draw[i].layerStack) &#123;   <span class="comment">//Layer栈</span></span><br><span class="line">                        display-&gt;setLayerStack(state.layerStack);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> ((state.orientation != draw[i].orientation) || </span><br><span class="line">                        (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) &#123;  <span class="comment">//旋转状态、viewport、frame帧</span></span><br><span class="line">                        display-&gt;setProjection(state.orientation, state.viewport, state.frame);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (state.width != draw[i].width || state.height != draw[i].height) &#123;  <span class="comment">//宽高</span></span><br><span class="line">                        display-&gt;setDisplaySize(state.width, state.height);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            ++i;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//找到新添加的Display设备（在current状态列表，而不再draw列表中）</span></span><br><span class="line">        <span class="comment">//即创建主屏对应的DisplayDevice</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; cc; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (draw.indexOfKey(curr.keyAt(i)) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="function"><span class="keyword">const</span> DisplayDeviceState&amp; <span class="title">state</span><span class="params">(curr[i])</span></span>;</span><br><span class="line"></span><br><span class="line">                sp&lt;compositionengine::DisplaySurface&gt; dispSurface;</span><br><span class="line">                sp&lt;IGraphicBufferProducer&gt; producer;</span><br><span class="line">                sp&lt;IGraphicBufferProducer&gt; bqProducer;</span><br><span class="line">                sp&lt;IGraphicBufferConsumer&gt; bqConsumer;</span><br><span class="line">                getFactory().createBufferQueue(&amp;bqProducer, &amp;bqConsumer, <span class="literal">false</span>);   <span class="comment">//创建BufferQueue</span></span><br><span class="line"></span><br><span class="line">                <span class="built_in">std</span>::optional&lt;DisplayId&gt; displayId;</span><br><span class="line">                <span class="comment">//虚拟显示设备</span></span><br><span class="line">                <span class="keyword">if</span> (state.isVirtual()) &#123;</span><br><span class="line">                    ......</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    ALOGE_IF(state.surface != <span class="literal">nullptr</span>,</span><br><span class="line">                             <span class="string">"adding a supported display, but rendering "</span></span><br><span class="line">                             <span class="string">"surface is provided (%p), ignoring it"</span>,</span><br><span class="line">                             state.surface.get());</span><br><span class="line">                    <span class="comment">//给新的显示设备添加Display id，并且创建framebufferSurface消费者</span></span><br><span class="line">                    displayId = state.displayId;</span><br><span class="line">                    LOG_ALWAYS_FATAL_IF(!displayId);</span><br><span class="line">                    dispSurface = <span class="keyword">new</span> FramebufferSurface(getHwComposer(), *displayId, bqConsumer);</span><br><span class="line">                    producer = bqProducer;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">const</span> wp&lt;IBinder&gt;&amp; displayToken = curr.keyAt(i);</span><br><span class="line">                <span class="keyword">if</span> (dispSurface != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">                    mDisplays.emplace(displayToken,</span><br><span class="line">                                      setupNewDisplayDeviceInternal(displayToken, displayId, state,</span><br><span class="line">                                                                    dispSurface, producer));</span><br><span class="line">                    <span class="keyword">if</span> (!state.isVirtual()) &#123;</span><br><span class="line">                        LOG_ALWAYS_FATAL_IF(!displayId);</span><br><span class="line">                        dispatchDisplayHotplugEvent(displayId-&gt;value, <span class="literal">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">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mDrawingState.displays = mCurrentState.displays;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h4 id="3-更新mCurrentState中Layer的transform-hint信息"><a href="#3-更新mCurrentState中Layer的transform-hint信息" class="headerlink" title="3. 更新mCurrentState中Layer的transform hint信息"></a>3. 更新mCurrentState中Layer的transform hint信息</h4><p><strong>第三部分：继续执行<code>handleTransactionLocked</code>函数，更新transform hint相关信息</strong></p><h4 id="4-更新Layer信息"><a href="#4-更新Layer信息" class="headerlink" title="4. 更新Layer信息"></a>4. 更新Layer信息</h4><p><strong>第四部分：更新Layer信息</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::handleTransactionLocked</span><span class="params">(<span class="keyword">uint32_t</span> transactionFlags)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//第三部分</span></span><br><span class="line">    <span class="comment">//当显示设备发生变化或者Layer发生变化，需要修改layer的transform hint，使得Layer视图的矩阵变化</span></span><br><span class="line">    <span class="keyword">if</span> (transactionFlags &amp; (eDisplayLayerStackChanged|eDisplayTransactionNeeded)) &#123;</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">//遍历mCurrentState所有layer</span></span><br><span class="line">        mCurrentState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">            ...</span><br><span class="line">            <span class="keyword">if</span> (!hintDisplay) &#123;</span><br><span class="line">                <span class="comment">//如果显示在过个显示设备上，则使用默认显示设备</span></span><br><span class="line">                hintDisplay = getDefaultDisplayDeviceLocked();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (hintDisplay) &#123;</span><br><span class="line">                <span class="comment">//更新layer的transform hint</span></span><br><span class="line">                layer-&gt;updateTransformHint(hintDisplay);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            first = <span class="literal">false</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">if</span> (mLayersAdded) &#123;</span><br><span class="line">        mLayersAdded = <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">// Layers have been added.</span></span><br><span class="line">        mVisibleRegionsDirty = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//=============================</span></span><br><span class="line">    <span class="comment">//第四部分：更新Layer信息</span></span><br><span class="line">    <span class="comment">//如果有Layer移除， 该Layer原先的显示区域就是需要更新显示区域</span></span><br><span class="line">    <span class="keyword">if</span> (mLayersRemoved) &#123;</span><br><span class="line">        mLayersRemoved = <span class="literal">false</span>;</span><br><span class="line">        mVisibleRegionsDirty = <span class="literal">true</span>;</span><br><span class="line">        mDrawingState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mLayersPendingRemoval.indexOf(layer) &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// this layer is not visible anymore</span></span><br><span class="line">                Region visibleReg;</span><br><span class="line">                visibleReg.<span class="built_in">set</span>(layer-&gt;getScreenBounds());</span><br><span class="line">                invalidateLayerStack(layer, visibleReg);</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">    commitInputWindowCommands();</span><br><span class="line">    <span class="comment">//所有变化的信息更新完成后，mCurrentState提交到mDrawingState中</span></span><br><span class="line">    commitTransaction();</span><br><span class="line">    &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="5-信息更新完成，mCurrentState提交到mDrawingState"><a href="#5-信息更新完成，mCurrentState提交到mDrawingState" class="headerlink" title="5. 信息更新完成，mCurrentState提交到mDrawingState"></a>5. 信息更新完成，mCurrentState提交到mDrawingState</h4><p>执行到commitTransaction函数就代表所有Layer信息更新完成，下一步开始合成显示这些变化后的内容（mDrawingState）</p><hr><h3 id="三、handleMessageInvalidate"><a href="#三、handleMessageInvalidate" class="headerlink" title="三、handleMessageInvalidate"></a>三、handleMessageInvalidate</h3><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">SurfaceFlinger::handleMessageInvalidate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    <span class="comment">//调用handlePageFlip，见下一小节的流程，该函数会从BufferQueue中获取Buffer</span></span><br><span class="line">    <span class="keyword">bool</span> refreshNeeded = handlePageFlip();</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : mLayersPendingRefresh) &#123;</span><br><span class="line">        Region visibleReg;</span><br><span class="line">        visibleReg.<span class="built_in">set</span>(layer-&gt;getScreenBounds());</span><br><span class="line">        <span class="comment">//见下</span></span><br><span class="line">        invalidateLayerStack(layer, visibleReg);</span><br><span class="line">    &#125;</span><br><span class="line">    mLayersPendingRefresh.clear();</span><br><span class="line">    <span class="keyword">return</span> refreshNeeded;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Android支持多个屏幕，layer可以定制化的只显示到某个显示屏幕上。其中就是靠layerStack(Layer栈)来实现的</span></span><br><span class="line"><span class="comment">//Layer的stack值如果和DisplayDevice的stack值一样，说明这个layer是属于这个显示屏幕的</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SurfaceFlinger::invalidateLayerStack</span><span class="params">(<span class="keyword">const</span> sp&lt;<span class="keyword">const</span> Layer&gt;&amp; layer, <span class="keyword">const</span> Region&amp; dirty)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> <span class="keyword">auto</span>&amp; [token, displayDevice] : mDisplays) &#123;</span><br><span class="line">        <span class="keyword">auto</span> display = displayDevice-&gt;getCompositionDisplay();</span><br><span class="line">        <span class="keyword">if</span> (display-&gt;belongsInOutput(layer-&gt;getLayerStack(), layer-&gt;getPrimaryDisplayOnly())) &#123;</span><br><span class="line">            display-&gt;editState().dirtyRegion.orSelf(dirty);</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><h4 id="handlePageFlip"><a href="#handlePageFlip" class="headerlink" title="handlePageFlip"></a>handlePageFlip</h4><p>mLayersWithQueuedFrames用于标记有Frame的Layer，详细处理查看上面对<code>BufferQueueLayer::onFrameAvailable</code>函数的注解。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">SurfaceFlinger::handlePageFlip</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    ALOGV(<span class="string">"handlePageFlip"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">nsecs_t</span> latchTime = systemTime();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> visibleRegions = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">bool</span> frameQueued = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">bool</span> newDataLatched = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//遍历mDrawingState的Layer，将需要合成的layer添加到mLayersWithQueuedFrames列表中</span></span><br><span class="line">    mDrawingState.traverseInZOrder([&amp;](Layer* layer) &#123;</span><br><span class="line">        <span class="keyword">if</span> (layer-&gt;hasReadyFrame()) &#123;</span><br><span class="line">            frameQueued = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">nsecs_t</span> expectedPresentTime;</span><br><span class="line">            expectedPresentTime = mScheduler-&gt;expectedPresentTime();</span><br><span class="line">            <span class="comment">//frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp:BufferQueueLayer::shouldPresentNow()</span></span><br><span class="line">            <span class="comment">//此处shouldPresentNow的判断逻辑：</span></span><br><span class="line">            <span class="comment">//1. 计算期望显示的时间，然后看Buffer的时间戳和期望显示的时间。</span></span><br><span class="line">            <span class="comment">//如果Buffer的时间还没到，且和期望显示的时间差不到1秒，则该shouldPresentNow逻辑成立</span></span><br><span class="line">            <span class="comment">//否则使用空的DamageRegion</span></span><br><span class="line">            <span class="keyword">if</span> (layer-&gt;shouldPresentNow(expectedPresentTime)) &#123;</span><br><span class="line">                <span class="comment">//</span></span><br><span class="line">                mLayersWithQueuedFrames.push_back(layer);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                ATRACE_NAME(<span class="string">"!layer-&gt;shouldPresentNow()"</span>);</span><br><span class="line">                layer-&gt;useEmptyDamage();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            layer-&gt;useEmptyDamage();</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> (!mLayersWithQueuedFrames.empty()) &#123;</span><br><span class="line">        <span class="function">Mutex::Autolock <span class="title">lock</span><span class="params">(mStateLock)</span></span>;</span><br><span class="line">        <span class="comment">//遍历mLayersWithQueuedFrames中的Layer</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; layer : mLayersWithQueuedFrames) &#123;</span><br><span class="line">            <span class="comment">//latchBuffer是从BufferQueue中获取Buffer，并将其绑定到Layer对应的纹理中</span></span><br><span class="line">            <span class="keyword">if</span> (layer-&gt;latchBuffer(visibleRegions, latchTime)) &#123;</span><br><span class="line">                <span class="comment">//添加到mLayersPendingRefresh列表中</span></span><br><span class="line">                mLayersPendingRefresh.push_back(layer);</span><br><span class="line">            &#125; </span><br><span class="line">            <span class="comment">//更新Surface的Damage</span></span><br><span class="line">            layer-&gt;useSurfaceDamage();</span><br><span class="line">            <span class="keyword">if</span> (layer-&gt;isBufferLatched()) &#123;</span><br><span class="line">                newDataLatched = <span class="literal">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">    mVisibleRegionsDirty |= visibleRegions;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//有BufferQueue过来，但是还没有到显示时间（mLayersWithQueuedFrames为空），或者没有获取到Buffer，则重新触发一次更新</span></span><br><span class="line">    <span class="keyword">if</span> (frameQueued &amp;&amp; (mLayersWithQueuedFrames.empty() || !newDataLatched)) &#123;</span><br><span class="line">        signalLayerUpdate();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// enter boot animation on first buffer latch</span></span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER &amp;&amp; newDataLatched)) &#123;</span><br><span class="line">        ALOGI(<span class="string">"Enter boot animation"</span>);</span><br><span class="line">        mBootStage = BootStage::BOOTANIMATION;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> !mLayersWithQueuedFrames.empty() &amp;&amp; newDataLatched;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Note:</strong>注意，所有的mLayersWithQueuedFrames都会走上面和下面的流程，每个Layer有自己的BufferLayerConsumer和BufferQueue。</p><h4 id="latchBuffer-gt-updateTexImage-gt-acquireBuffer"><a href="#latchBuffer-gt-updateTexImage-gt-acquireBuffer" class="headerlink" title="latchBuffer-&gt;updateTexImage-&gt;acquireBuffer"></a>latchBuffer-&gt;updateTexImage-&gt;acquireBuffer</h4><blockquote><p>详细流程可参考：<a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/#BufferQueue" target="_blank" rel="noopener">Android 图形显示框架之BufferQueue-acquire&amp;release</a></p></blockquote><p>该函数中调用<code>updateTexImage</code>，而这个关键函数回去获取Buffer。</p><p>拿到Buffer后，将Buffer保存在<code>mSlots[slot].mGraphicBuffer</code>中。同时更新mFrameNumber和mFence。</p><p>该函数<code>updateTexImage</code>有几种返回处理结果：</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/BufferQueueLayer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">status_t</span> <span class="title">BufferQueueLayer::updateTexImage</span><span class="params">(<span class="keyword">bool</span>&amp; recomputeVisibleRegions, <span class="keyword">nsecs_t</span> latchTime)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">status_t</span> updateResult = mConsumer-&gt;updateTexImage(&amp;r, expectedPresentTime, &amp;mAutoRefresh,</span><br><span class="line">                                                      &amp;queuedBuffer, maxFrameNumberToAcquire);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//PRESENT_LATER：稍后显示，暂时不显示，并且触发SurfaceFlinger刷新（更新Layer数据）</span></span><br><span class="line">    <span class="keyword">if</span> (updateResult == BufferQueue::PRESENT_LATER) &#123;</span><br><span class="line">        mFlinger-&gt;signalLayerUpdate();</span><br><span class="line">        <span class="keyword">return</span> BAD_VALUE;</span><br><span class="line">    <span class="comment">//BUFFER_REJECTED：Buffer被Reject掉，这一帧数据不再显示，从mQueueItems中删除这一帧，同时mQueuedFrames减一</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (updateResult == BufferLayerConsumer::BUFFER_REJECTED) &#123;</span><br><span class="line">        <span class="keyword">if</span> (queuedBuffer) &#123;</span><br><span class="line">            Mutex::Autolock lock(mQueueItemLock);</span><br><span class="line">            mFlinger-&gt;mTimeStats-&gt;removeTimeRecord(layerID, mQueueItems[<span class="number">0</span>].mFrameNumber);</span><br><span class="line">            mQueueItems.removeAt(<span class="number">0</span>);</span><br><span class="line">            mQueuedFrames--;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> BAD_VALUE;</span><br><span class="line">    <span class="comment">//更新失败或出错</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (updateResult != NO_ERROR || mUpdateTexImageFailed) &#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>调用BufferLayerConsumer的updateTexImage，在acquireBufferLocked请求Buffer后，释放上一个Buffer，更新Buffer</p><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/BufferLayerConsumer.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="function"><span class="keyword">status_t</span> <span class="title">BufferLayerConsumer::updateTexImage</span><span class="params">(BufferRejecter* rejecter, <span class="keyword">nsecs_t</span> expectedPresentTime,</span></span></span><br><span class="line"><span class="function"><span class="params">                                             <span class="keyword">bool</span>* autoRefresh, <span class="keyword">bool</span>* queuedBuffer,</span></span></span><br><span class="line"><span class="function"><span class="params">                                             <span class="keyword">uint64_t</span> maxFrameNumber)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">status_t</span> err = acquireBufferLocked(&amp;item, expectedPresentTime, maxFrameNumber);</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//释放上一个Buffer， 更新Buffer</span></span><br><span class="line">    err = updateAndReleaseLocked(item, &amp;mPendingRelease);</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">status_t</span> <span class="title">BufferLayerConsumer::updateAndReleaseLocked</span><span class="params">(<span class="keyword">const</span> BufferItem&amp; item,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                     PendingRelease* pendingRelease)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> slot = item.mSlot;                                                        </span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">//释放旧的Buffer - release old buffer</span></span><br><span class="line">    <span class="keyword">if</span> (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) &#123;</span><br><span class="line">        <span class="keyword">if</span> (pendingRelease == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">            <span class="keyword">status_t</span> status =</span><br><span class="line">                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer-&gt;graphicBuffer());</span><br><span class="line">            <span class="keyword">if</span> (status &lt; NO_ERROR) &#123;</span><br><span class="line">                BLC_LOGE(<span class="string">"updateAndRelease: failed to release buffer: %s (%d)"</span>, strerror(-status),</span><br><span class="line">                         status);</span><br><span class="line">                err = status;</span><br><span class="line">                <span class="comment">// keep going, with error raised [?]</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            pendingRelease-&gt;currentTexture = mCurrentTexture;</span><br><span class="line">            pendingRelease-&gt;graphicBuffer = mCurrentTextureBuffer-&gt;graphicBuffer();</span><br><span class="line">            pendingRelease-&gt;isPending = <span class="literal">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">//更新Buffer - Update the BufferLayerConsumer state.</span></span><br><span class="line">    mCurrentTexture = slot;</span><br><span class="line">    mCurrentTextureBuffer = nextTextureBuffer;</span><br><span class="line">    mCurrentCrop = item.mCrop;</span><br><span class="line">    mCurrentTransform = item.mTransform;</span><br><span class="line">    mCurrentScalingMode = item.mScalingMode;</span><br><span class="line">    mCurrentTimestamp = item.mTimestamp;</span><br><span class="line">    mCurrentDataSpace = <span class="keyword">static_cast</span>&lt;ui::Dataspace&gt;(item.mDataSpace);</span><br><span class="line">    mCurrentHdrMetadata = item.mHdrMetadata;</span><br><span class="line">    mCurrentFence = item.mFence;</span><br><span class="line">    mCurrentFenceTime = item.mFenceTime;</span><br><span class="line">    mCurrentFrameNumber = item.mFrameNumber;</span><br><span class="line">    mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;</span><br><span class="line">    mCurrentSurfaceDamage = item.mSurfaceDamage;</span><br><span class="line">    mCurrentApi = item.mApi;</span><br><span class="line"></span><br><span class="line">    computeCurrentTransformMatrixLocked();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> err;</span><br></pre></td></tr></table></figure><p>然后将通过消费者<code>acquireBufferLocked</code>函数请求Buffer。</p><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/gui/ConsumerBase.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">status_t</span> <span class="title">ConsumerBase::acquireBufferLocked</span><span class="params">(BufferItem *item,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">nsecs_t</span> presentWhen, <span class="keyword">uint64_t</span> maxFrameNumber)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mAbandoned) &#123;</span><br><span class="line">        CB_LOGE(<span class="string">"acquireBufferLocked: ConsumerBase is abandoned!"</span>);</span><br><span class="line">        <span class="keyword">return</span> NO_INIT;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">status_t</span> err = mConsumer-&gt;acquireBuffer(item, presentWhen, maxFrameNumber);</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="四、Invalidate流程小结（正式合成刷新前的准备工作）"><a href="#四、Invalidate流程小结（正式合成刷新前的准备工作）" class="headerlink" title="四、Invalidate流程小结（正式合成刷新前的准备工作）"></a>四、Invalidate流程小结（正式合成刷新前的准备工作）</h3><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">SurfaceFlinger::onMessageReceived</span><span class="params">(<span class="keyword">int32_t</span> what)</span> NO_THREAD_SAFETY_ANALYSIS </span>&#123;</span><br><span class="line">    <span class="comment">//systrace抓取</span></span><br><span class="line">    ATRACE_CALL();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">switch</span> (what) &#123;</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::INVALIDATE: &#123;</span><br><span class="line">            ......</span><br><span class="line">            <span class="comment">//处理Transition，在合成前更新Layer的信息</span></span><br><span class="line">            <span class="keyword">bool</span> refreshNeeded = handleMessageTransaction();</span><br><span class="line">            <span class="comment">//</span></span><br><span class="line">            refreshNeeded |= handleMessageInvalidate();</span><br><span class="line"></span><br><span class="line">            updateCursorAsync();</span><br><span class="line">            updateInputFlinger();</span><br><span class="line"></span><br><span class="line">            refreshNeeded |= mRepaintEverything;</span><br><span class="line">            <span class="keyword">if</span> (refreshNeeded &amp;&amp; CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) &#123;</span><br><span class="line">                <span class="comment">//如果Transaction事务修改了窗口状态，则发出刷新信号</span></span><br><span class="line">                <span class="comment">//一个新的buffer将被请求</span></span><br><span class="line">                signalRefresh();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">case</span> MessageQueue::REFRESH: &#123;</span><br><span class="line">            handleMessageRefresh();</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的分析就是INVALIDATE的基本流程，该过程主要处理SurfaceFlinger距上次合成后的一些变化信息：</p><ol><li>handleMessageTransaction处理Layer属性变化、显示设备变化、更新显示设备的transform hint信息、处理Layer移除和增加等相关的信息等情况，将变化信息mCurrentState提交到mDrawingState，等待合成处理</li><li>handleMessageInvalidate更新了Layer的Buffer内容（通过LatchBuffer函数从BufferQueue中获取）到Layer的纹理</li><li>mRepaintEverything表示HWC硬件要求强制刷新</li></ol><hr><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><ul><li><a href="https://www.jianshu.com/p/fa115146949f" target="_blank" rel="noopener">SurfaceFlinger合成流程(一)</a></li><li><a href="https://www.jianshu.com/p/fd16dcb4dfb6" target="_blank" rel="noopener">SurfaceFlinger合成流程(二)</a></li><li><a href="https://wizzie.top/Blog/2019/09/22/2019/190922-android-handler-cpp/#SurfaceFlinger%E7%9A%84%E6%B6%88%E6%81%AF%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6" target="_blank" rel="noopener">Android Handler消息循环处理机制</a></li><li><a href="https://wizzie.top/Blog/2020/07/30/2020/200730_android_GraphicsFramework/" target="_blank" rel="noopener">Android 图形显示框架</a></li><li><a href="https://blog.csdn.net/u013686019/article/details/51614774" target="_blank" rel="noopener">Android BitTube</a></li><li><a href="https://blog.csdn.net/dabenxiong666/article/details/80629316" target="_blank" rel="noopener">Android之BitTube</a></li><li><a href="https://blog.csdn.net/weixin_41054077/article/details/105735639" target="_blank" rel="noopener">基于Android Q分析SurfaceFlinger启动过程</a></li><li><a href="https://wizzie.top/Blog/2019/12/22/2019/191222_android_HWC2/#mCurrentState%E5%92%8CmDrawingState" target="_blank" rel="noopener">Android SurfaceFlinger和HWC2概述 - mCurrentState和mDrawingState</a></li><li><a href="https://www.jianshu.com/p/b0928eaaeb1c" target="_blank" rel="noopener">SurfaceFlinger图像合成[1]</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;在HWUI渲染完成后，Buffer会在&lt;code&gt;frameworks/native/libs/gui/BufferQueueProducer.cpp&lt;/code&gt;中通过queueBuffer放入BufferQueue，在该函数中调用&lt;code&gt;frameAvailableListener-&amp;gt;onFrameAvailable(item)&lt;/code&gt;通知consumer消费者，从而触发SurfaceFlinger合成。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android protobuf(.proto文件)</title>
    <link href="https://alonealive.github.io/Blog/2020/08/15/2020/200815_android_protobuf/"/>
    <id>https://alonealive.github.io/Blog/2020/08/15/2020/200815_android_protobuf/</id>
    <published>2020-08-15T13:42:00.000Z</published>
    <updated>2020-08-16T11:49:53.973Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>最近碰到一个关于<code>frameworks/base/core/proto/android/providers/settings/secure.proto</code>文件中修改某个属性的问题，所以针对protobuf(.proto文件)进行一个学习了解。<br>Google Protocol Buffers，（简称Protobuf，协议缓冲区），类似json或XML，Google开源的支持多语言、跨平台的结构化数据序列化项目，但是比它们更小、更快、更简单。<br>Protobuf通过编写proto文件来定义消息格式或RPC服务定义。</p></blockquote><a id="more"></a><h2 id="proto模块介绍"><a href="#proto模块介绍" class="headerlink" title="proto模块介绍"></a>proto模块介绍</h2><p>在Framework模块中有个单独的模块：<code>frameworks/base/core/proto/</code></p><p>查看该模块下的README.md和OWNDER文件有简单介绍。OWNER劝告开发人员需要熟悉该功能，谨慎修改。</p><h3 id="README-md文件内容"><a href="#README-md文件内容" class="headerlink" title="README.md文件内容"></a>README.md文件内容</h3><ol><li>Android其他版本中，使用四个空格缩进，而非两个</li><li>基于Java文件的protos文件，使用该Java文件相同名称的包。例如<code>com.android.server.thing</code>代替<code>com.android.server.thing.proto</code></li><li>如果proto描述了dumpsys的顶级输出，他就应该包含dump。这样更容易理解他是作为某个服务的dump输出，而不是该服务的数据结构。例如<code>WindowManagerServiceDumpProto</code>和<code>WindowManagerServiceProto</code></li></ol><ul><li>含有poroto后缀的message名称，他的内嵌的message的名称不需要有proto后缀，例如：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">message FooProto &#123;</span><br><span class="line">    message Bar &#123;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&amp;&amp;</span><br><span class="line"></span><br><span class="line">message FooProto &#123;</span><br><span class="line">    message BarProto &#123;  <span class="comment">//没必要有proto后缀</span></span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>如果proto代表一个对象的结构，后缀应该要有<code>proto</code>。并且还请包含该原是对象的完整包路径，来作为对这个proto message的注释。</p><ol start="4"><li><p>在字段中包含单位名字，例如：<code>screen_time_ms</code> VS <code>screen_time</code>, or <code>file_size_bytes</code> or <code>file_size_mebibytes</code> VS <code>file_size</code></p></li><li><p>保留字段号50000-100000，供原始设备制造商使用。</p></li></ol><hr><h2 id="编写proto文件"><a href="#编写proto文件" class="headerlink" title="编写proto文件"></a>编写proto文件</h2><h3 id="范例helloworld-proto"><a href="#范例helloworld-proto" class="headerlink" title="范例helloworld.proto"></a>范例helloworld.proto</h3><blockquote><p>代码在<a href="https://github.com/grpc/grpc/blob/master/examples/protos/helloworld.proto" target="_blank" rel="noopener">https://github.com/grpc/grpc/blob/master/examples/protos/helloworld.proto</a></p></blockquote><figure class="highlight plain"><figcaption><span>helloworld.proto</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">&#x2F;&#x2F;版本号，默认版本是2</span><br><span class="line">syntax &#x3D; &quot;proto3&quot;;</span><br><span class="line"> </span><br><span class="line">option java_multiple_files &#x3D; true;</span><br><span class="line">option java_package &#x3D; &quot;io.grpc.examples.helloworld&quot;;</span><br><span class="line">option java_outer_classname &#x3D; &quot;HelloWorldProto&quot;;</span><br><span class="line">option objc_class_prefix &#x3D; &quot;HLW&quot;;</span><br><span class="line"> </span><br><span class="line">package helloworld;</span><br><span class="line"> </span><br><span class="line">&#x2F;&#x2F; The greeting service definition.</span><br><span class="line">service Greeter &#123;</span><br><span class="line">  &#x2F;&#x2F; Sends a greeting</span><br><span class="line">  rpc SayHello (HelloRequest) returns (HelloReply) &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line">&#x2F;&#x2F; The request message containing the user&#39;s name.</span><br><span class="line">&#x2F;&#x2F; 一个message类型看上去像是Java class，由多个字段组成</span><br><span class="line">&#x2F;&#x2F;每一个字段都由类型、名称组成，位于等号右边的值不是字段默认值，而是字段编号，可以理解为字段身份的标识符，类似于数据库中的主键，不可重复</span><br><span class="line">&#x2F;&#x2F;标识符用于在编译后的二进制消息格式中对字段进行识别，一旦投入使用，字段的标识就不应该再改变。</span><br><span class="line">&#x2F;&#x2F;数字标签的范围是[1, 536870911]，其中19000～19999是保留数字。</span><br><span class="line">message HelloRequest &#123;</span><br><span class="line">  string name &#x3D; 1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; The response message containing the greetings</span><br><span class="line">message HelloReply &#123;</span><br><span class="line">  string message &#x3D; 1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message SearchRequest &#123;</span><br><span class="line">  required string query &#x3D; 1; </span><br><span class="line">  required int32 page_number &#x3D; 2;</span><br><span class="line">  optional int32 result_per_page &#x3D; 3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="protobuf和其他编程语言的类型比较"><a href="#protobuf和其他编程语言的类型比较" class="headerlink" title="protobuf和其他编程语言的类型比较"></a>protobuf和其他编程语言的类型比较</h3><p><img src="protoType.png" alt="protobuf和其他编程语言的类型比较"></p><h3 id="字段编号"><a href="#字段编号" class="headerlink" title="字段编号"></a>字段编号</h3><p>字段编号是Protobuf的重要组成部分。它们用于标识二进制编码数据中的字段，这意味着它们不能从版本更改为服务版本。优点在于可以实现向后兼容性和向前兼容性。只要处理丢失值的可能性，客户端和服务就会忽略他们不知道的字段编号。</p><p>在二进制格式中，字段号与类型标识符组合在一起。1到15之间的字段编号可以使用其类型编码为单字节。从16到2047的数字需要2个字节。如果出于任何原因需要将超过2047个字段，则可以更高。字段号1到15的单字节标识符提供更好的性能，因此，你应将其用于最基本的常用字段。</p><h3 id="限定符说明"><a href="#限定符说明" class="headerlink" title="限定符说明"></a>限定符说明</h3><table><thead><tr><th align="center">限定符</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">required</td><td align="center">必须字段，必须设置该字段的值</td></tr><tr><td align="center">optional</td><td align="center">可选字段，有选择性的设置或者不设置该字段的值</td></tr><tr><td align="center">repeated</td><td align="center">表示该字段可以被重复任意多次包含</td></tr></tbody></table><h3 id="包package"><a href="#包package" class="headerlink" title="包package"></a>包package</h3><p>可以用指定package以避免类型命名冲突：</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">package foo.bar;</span><br><span class="line">message Open &#123; ... &#125;</span><br></pre></td></tr></table></figure><p>也可以用类型的全限定名来引用它：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">message Foo &#123;</span><br><span class="line">  ...</span><br><span class="line">  foo.bar.Open open &#x3D; 1;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Note:</strong>指定包名后，会对生成的代码产生影响，以Java为例，生成的类会以你指定的package作为包名。</p><h3 id="修改规则"><a href="#修改规则" class="headerlink" title="修改规则"></a>修改规则</h3><p>如果修改正在使用的proto文件，比如为类型增加一个字段，protobuf支持这种修改而不影响已有的服务，不过需要遵循一定的规则：</p><ol><li>不改变已有字段的字段编号 </li><li>当增加一个新的字段的时候，老系统序列化后的数据依然可以被新的格式所解析，只不过需要处理新加字段的缺省值。 </li><li>字段也可以被移除，但是建议<code>Reserved</code>这个字段，避免将来会使用这个字段 </li><li>int32, uint32, int64, uint64 和 bool类型都是兼容的 </li><li>sint32 和 sint64兼容，但是不和其它整数类型兼容 </li><li>string 和 bytes兼容，如果 bytes 是合法的UTF-8 bytes的话 </li><li>嵌入类型和bytes兼容，如果bytes包含一个消息的编码版本的话 </li><li>fixed32和sfixed32, fixed64和sfixed64 enum和int32, uint32, int64, uint64格式兼容</li></ol><h3 id="嵌套类型"><a href="#嵌套类型" class="headerlink" title="嵌套类型"></a>嵌套类型</h3><p>通过协议缓冲区（Protobuf）可以在其他消息中嵌套消息定义。 下面的示例演示如何创建嵌套消息类型：</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></pre></td><td class="code"><pre><span class="line">message Outer &#123;</span><br><span class="line">    message Inner &#123;</span><br><span class="line">        string text &#x3D; 1;</span><br><span class="line">    &#125;</span><br><span class="line">    Inner inner &#x3D; 1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="repeated修饰符"><a href="#repeated修饰符" class="headerlink" title="repeated修饰符"></a>repeated修饰符</h3><p>如果一个字段被repeated修饰，则表示它是一个列表类型的字段，如下所示：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">message SearchRequest &#123;</span><br><span class="line">  repeated string args &#x3D; 1 &#x2F;&#x2F; 等价于java中的List&lt;string&gt; args</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果希望可以预留一些数字标签或者字段可以使用reserved修饰符：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">message Foo &#123;</span><br><span class="line">  reserved 2, 15, 9 to 11;</span><br><span class="line">  reserved &quot;foo&quot;, &quot;bar&quot;;</span><br><span class="line">  &#x2F;&#x2F; string foo &#x3D; 3  &#x2F;&#x2F;编译报错，因为‘foo’已经被标为保留字段</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="保留字段reserved"><a href="#保留字段reserved" class="headerlink" title="保留字段reserved"></a>保留字段reserved</h3><p>Protobuf中的向后兼容性保证依赖于始终表示相同数据项的字段编号。如果从服务新版本中的消息中删除了字段，则不应重复使用该字段编号。</p><p>可以使用reserved关键字强制执行此情况。</p><p>如果定义Stock的消息中删除某字段，则应保留其字段编号，如以下示例所示。</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></pre></td><td class="code"><pre><span class="line">syntax &quot;proto3&quot;;</span><br><span class="line"></span><br><span class="line">message Stock &#123;</span><br><span class="line"></span><br><span class="line">    reserved 3, 4;</span><br><span class="line">    int32 id &#x3D; 1;</span><br><span class="line">    string symbol &#x3D; 2;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>还可以将reserved关键字用作将来可能添加的字段的占位符。 您可以使用<code>to</code>关键字将连续字段数表示为范围。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">syntax &quot;proto3&quot;;</span><br><span class="line"></span><br><span class="line">message Info &#123;</span><br><span class="line"></span><br><span class="line">    reserved 2, 9 to 11, 15;</span><br><span class="line">    &#x2F;&#x2F; ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Any和OneOf字段"><a href="#Any和OneOf字段" class="headerlink" title="Any和OneOf字段"></a>Any和OneOf字段</h3><p>Protobuf提供了两个用于处理可能属于多个类型的值的更简单选项。</p><p>Any类型可以表示任何已知的Protobuf消息类型。</p><p>使用oneof关键字来指定在任何消息中只能设置一个字段范围中的一个字段。</p><ol><li>Any是Protobuf的”已知类型”：一系列有用的可重复使用的消息类型，具有所有支持语言的实现。若要使用Any类型，必须导入<code>google/protobuf/any.proto</code>定义</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">syntax &quot;proto3&quot;</span><br><span class="line"></span><br><span class="line">import &quot;google&#x2F;protobuf&#x2F;any.proto&quot;</span><br><span class="line"></span><br><span class="line">message Stock &#123;</span><br><span class="line">    &#x2F;&#x2F; Stock-specific data</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message Currency &#123;</span><br><span class="line">    &#x2F;&#x2F; Currency-specific data</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message ChangeNotification &#123;</span><br><span class="line">    int32 id &#x3D; 1;</span><br><span class="line">    google.protobuf.Any instrument &#x3D; 2;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>Oneof是一项语言功能：编译器在生成message类时处理oneof关键字。使用oneof指定ChangeNotification消息可能如下所示：</li></ol><p>在整个消息声明中，oneof集内的字段必须具有唯一的字段编号。</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></pre></td><td class="code"><pre><span class="line">message Stock &#123;</span><br><span class="line">    &#x2F;&#x2F; Stock-specific data</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message Currency &#123;</span><br><span class="line">    &#x2F;&#x2F; Currency-specific data</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message ChangeNotification &#123;</span><br><span class="line">  int32 id &#x3D; 1;</span><br><span class="line">  oneof instrument &#123;</span><br><span class="line">    Stock stock &#x3D; 2;</span><br><span class="line">    Currency currency &#x3D; 3;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Note:</strong></p><ul><li>设置属于oneof集一部分的任何字段都将自动清除该集中的任何其他字段。不能将repeated与oneof一起使用。相反，可以创建包含重复字段或oneof集的嵌套消息，以解决此限制。</li><li>oneof块中的字段不支持repeated。</li></ul><h3 id="枚举"><a href="#枚举" class="headerlink" title="枚举"></a>枚举</h3><p>上面使用枚举来确定Oneof字段的类型。可以定义自己的枚举类型，Protobuf将它们编译为C#（或者其他语言）枚举类型。</p><p>由于Protobuf可以用于各种语言，因此枚举的命名约定与C#约定不同。</p><p>但是，代码生成器将名称转换为传统的C#大小写。如果字段名称的Pascal大小写以枚举名称开头，则将其删除。</p><p>例如，在下面的Protobuf枚举中，字段用预缀为ACCOUNT_STATUS。</p><p><strong>第一个枚举值的数值必须是0且至少有一个枚举值，否则编译报错。编译后编译器会为你生成对应语言的枚举类。</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">enum AccountStatus &#123;</span><br><span class="line">  ACCOUNT_STATUS_UNKNOWN &#x3D; 0;</span><br><span class="line">  ACCOUNT_STATUS_PENDING &#x3D; 1;</span><br><span class="line">  ACCOUNT_STATUS_ACTIVE &#x3D; 2;</span><br><span class="line">  ACCOUNT_STATUS_SUSPENDED &#x3D; 3;</span><br><span class="line">  ACCOUNT_STATUS_CLOSED &#x3D; 4;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由于编码原因，出于效率考虑，官方不推荐使用负数作为枚举值的数值。</p><h3 id="类型默认值"><a href="#类型默认值" class="headerlink" title="类型默认值"></a>类型默认值</h3><ol><li>string类型的默认值是空字符串</li><li>bytes类型的默认值是空字节</li><li>bool类型的默认值是false</li><li>数字类型的默认值是0</li><li>enum类型的默认值是第一个定义的枚举值</li><li>message类型的默认值与语言相关</li><li>repeated修饰的字段默认值是空列表</li></ol><h3 id="import-public的传递引用功能"><a href="#import-public的传递引用功能" class="headerlink" title="import public的传递引用功能"></a>import public的传递引用功能</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;</span><br><span class="line">&#x2F;&#x2F; 文件：new.proto</span><br><span class="line">&#x2F;&#x2F; 原来在old.proto文件中的定义移到这里</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;</span><br><span class="line">&#x2F;&#x2F; 文件：old.proto</span><br><span class="line">import public &quot;new.proto&quot;; &#x2F;&#x2F; 把引用传递给上层使用方</span><br><span class="line">import &quot;other.proto&quot;; &#x2F;&#x2F; 引用old.proto本身使用的定义</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;&#x2F;</span><br><span class="line">&#x2F;&#x2F; 文件：client.proto</span><br><span class="line">import &quot;old.proto&quot;;</span><br><span class="line">&#x2F;&#x2F; 此处可以引用old.proto和new.proto中的定义，但不能使用other.proto中的定义</span><br></pre></td></tr></table></figure><p>从这个例子中可以看到<code>import</code>关键字导入的定义仅在当前文件有效，不能被上层使用方引用（client.proto无法使用other.proto中的定义）</p><p>而<code>import public</code>关键字导入的定义可以被上层使用方引用（client.proto可以使用new.proto中的定义），<code>import public</code>的功能可以看作是import的超集，在import的功能上还具有传递引用的作用。</p><h3 id="option选项"><a href="#option选项" class="headerlink" title="option选项"></a>option选项</h3><p>选项不对message的定义产生任何的效果，只会在一些特定的场景中起到作用：</p><ol><li><code>option java_package = &quot;com.example.foo&quot;;</code> 编译器为以此作为生成的Java类的包名，如果没有该选项，则会以pb的package作为包名。</li><li><code>option java_multiple_files = true;</code> 该选项为true时，生成的Java类将是包级别的，否则会在一个包装类中。</li><li><code>option optimize_for = CODE_SIZE;</code> 该选项会对生成的类产生影响，作用是根据指定的选项对代码进行不同方面的优化。</li><li><code>int32 old_field = 6 [deprecated=true];</code> 把字段标为过时的。</li></ol><hr><h2 id="protoBuf缺点"><a href="#protoBuf缺点" class="headerlink" title="protoBuf缺点"></a>protoBuf缺点</h2><p>Protbuf相比XML来说，主要优点是性能高。也有不足之处，功能简单，无法用来表示复杂的概念。</p><p>由于文本并不适合用来描述数据结构，所以Protobuf也不适合用来对基于文本的标记文档（如HTML建模。另外，由于XML具有某种程度上的自解释性，它可以被人直接读取编辑，在这一点上Protobuf以二进制的方式存储，除非有<code>.proto</code>定义，否则无法直接读出Protobuf的任何内容。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><ul><li><a href="https://developers.google.com/protocol-buffers/docs/overview" target="_blank" rel="noopener">Google官方文档</a></li><li><a href="https://developers.google.com/protocol-buffers/docs/overview" target="_blank" rel="noopener">Protobuf Github项目文档地址</a></li><li><a href="https://docs.microsoft.com/zh-cn/dotnet/architecture/grpc-for-wcf-developers/protobuf-reserved" target="_blank" rel="noopener">Protobuf 保留字段</a></li><li><a href="https://studygolang.com/articles/2540" target="_blank" rel="noopener">在Golang中安装使用Protobuf</a></li><li><a href="https://www.jianshu.com/p/ea656dc9b037" target="_blank" rel="noopener">Protobuf3学习笔记</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;最近碰到一个关于&lt;code&gt;frameworks/base/core/proto/android/providers/settings/secure.proto&lt;/code&gt;文件中修改某个属性的问题，所以针对protobuf(.proto文件)进行一个学习了解。&lt;br&gt;Google Protocol Buffers，（简称Protobuf，协议缓冲区），类似json或XML，Google开源的支持多语言、跨平台的结构化数据序列化项目，但是比它们更小、更快、更简单。&lt;br&gt;Protobuf通过编写proto文件来定义消息格式或RPC服务定义。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
  </entry>
  
  <entry>
    <title>Android ANR基本Log分析</title>
    <link href="https://alonealive.github.io/Blog/2020/08/06/2020/200806_android_ANR_BaseLog/"/>
    <id>https://alonealive.github.io/Blog/2020/08/06/2020/200806_android_ANR_BaseLog/</id>
    <published>2020-08-06T13:52:00.000Z</published>
    <updated>2020-08-07T13:40:32.855Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>ANR（Application Not Responding），字面意思是应用无响应，即用户的一些操作无法从应用中获取反馈。关于发生ANR的trace.txt文件的请参考<a href="https://wizzie.top/Blog/2020/06/11/2020/200611_android_tracetxt/" target="_blank" rel="noopener">Android ANR traces.txt文件分析</a></p></blockquote><a id="more"></a><h2 id="触发原因"><a href="#触发原因" class="headerlink" title="触发原因"></a>触发原因</h2><p>Android系统中的应用被Activity Manager及Window Manager两个系统服务监控着，Android系统会在如下情况触发ANR：</p><ul><li>Input事件超过5s没有被处理完，即5秒内无法对输入事件（按键及触摸）做出响应</li><li>Service处理超时，前台20s，后台200s</li><li>BroadcastReceiver（广播接收器）处理超时，前台10S，后台60s</li><li>ContentProvider执行超时，比较少见</li></ul><p>出现ANR之后一个直观现象就是系统会展示出一个ANR弹框。</p><p>从发生的原因分：</p><ul><li>主线程有耗时操作，如有复杂的layout布局，IO操作等。</li><li>被Binder对端block</li><li>被子线程同步锁block</li><li>Binder被占满导致主线程无法和SystemServer通信</li><li>得不到系统资源（CPU/RAM/IO）</li></ul><p>从进程的角度分：</p><ul><li>问题出在当前进程:</li><li>主线程本身耗时, 或则主线程的消息队列存在耗时操作;</li><li>主线程被本进程的其他子线程所blocked;</li><li>问题出在远端进程(一般是binder call或socket等通信方式)</li></ul><h2 id="基本log解读"><a href="#基本log解读" class="headerlink" title="基本log解读"></a>基本log解读</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;进程是30970</span><br><span class="line">&#x2F;&#x2F;特殊情况下，如果PID是0，说明发生ANR之前，这个进程被LowMemoryKiller杀死了或者出现了Crash。这种情况下，是无法接收到系统的广播或者按键消息的，故而出现ANR</span><br><span class="line">&#x2F;&#x2F;ANR具体发生的包名</span><br><span class="line">08-01 19:17:05.155  1000  1304  1328 I am_anr  : [0,30970,com.android.systemui,551042573,Input dispatching timed out (StatusBar, Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 54.  Wait queue head age: 9044.8ms.)]</span><br><span class="line">&#x2F;&#x2F;ANR具体发生的包名</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager: ANR in com.android.systemui</span><br><span class="line">&#x2F;&#x2F;ANR发生的原因是Input dispatching timed out</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager: PID: 30970</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager: Reason: Input dispatching timed out (StatusBar, Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 54.  Wait queue head age: 9044.8ms.)</span><br><span class="line">&#x2F;&#x2F;三个数字分别是1分钟、5分钟、15分钟内系统的平均负荷</span><br><span class="line">&#x2F;&#x2F;当CPU完全空闲的时候，平均负荷为0；当CPU工作量饱和的时候，平均负荷为1，通过Load可以判断系统负荷是否过重</span><br><span class="line">&#x2F;&#x2F;大致可以这样区分：</span><br><span class="line">&#x2F;&#x2F;当系统负荷持续大于0.7，你必须开始调查了，问题出在哪里，防止情况恶化。</span><br><span class="line">&#x2F;&#x2F;当系统负荷持续大于1.0，你必须动手寻找解决办法，把这个值降下来。</span><br><span class="line">&#x2F;&#x2F;当系统负荷达到5.0，就表明你的系统有很严重的问题</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager: Load: 46.53 &#x2F; 37.82 &#x2F; 34.77</span><br><span class="line">&#x2F;&#x2F;ANR发生的时候，Top进程的Cpu占用情况，user代表是用户空间，kernel是内核空间</span><br><span class="line">&#x2F;&#x2F;查看每个CPU的使用频度：adb shell cat &#x2F;sys&#x2F;devices&#x2F;system&#x2F;cpu&#x2F;cpu1&#x2F;cpufreq&#x2F;stats&#x2F;time_in_state</span><br><span class="line">&#x2F;&#x2F;一般如下规律：</span><br><span class="line">&#x2F;&#x2F;1. kswapd0 cpu占用率偏高，系统整体运行会缓慢，从而引起各种ANR。把问题转给&quot;内存优化&quot;，请他们进行优化</span><br><span class="line">&#x2F;&#x2F;2. logd　CPU占用率偏高，也会引起系统卡顿和ANR，因为各个进程输出LOG的操作被阻塞从而执行的极为缓慢</span><br><span class="line">&#x2F;&#x2F;3. Vold占用CPU过高，会引起系统卡顿和ANR，请负责存储的同学先调查</span><br><span class="line">&#x2F;&#x2F;4. qcom.sensor CPU占用率过高，会引起卡顿，请系统同学调查</span><br><span class="line">&#x2F;&#x2F;5. 应用自身CPU占用率较高，高概率应用自身问题</span><br><span class="line">&#x2F;&#x2F;6. 应用处于D状态，发生ANR，如果最后的操作是refriger，那么是应用被冻结了，正常情况下是功耗优化引起的。</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager: CPU usage from 0ms to 23321ms later (2020-08-01 19:17:04.850 to 2020-08-01 19:17:28.171) with 99% awake:</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   36% 30970&#x2F;com.android.systemui: 25% user + 10% kernel &#x2F; faults: 11945 minor 24 major</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   35% 20903&#x2F;com.tencent.mobileqq:video: 28% user + 6.4% kernel &#x2F; faults: 6413 minor 23 major</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   28% 555&#x2F;surfaceflinger: 16% user + 12% kernel &#x2F; faults: 1846 minor 2 major</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   26% 498&#x2F;android.hardware.audio@5.0-service-***: 21% user + 4.9% kernel &#x2F; faults: 13 minor</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   18% 1304&#x2F;system_server: 9.5% user + 8.7% kernel &#x2F; faults: 9666 minor 329 major</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   17% 3919&#x2F;com.***.service: 10% user + 6.7% kernel &#x2F; faults: 8604 minor 30 major</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   16% 28827&#x2F;com.tencent.qqmusic: 9.2% user + 7.3% kernel &#x2F; faults: 9299 minor 18 major</span><br><span class="line">08-01 19:17:28.258  1000  1304  1328 E ActivityManager:   16% 142&#x2F;kswapd0: 0% user + 16% kernel</span><br><span class="line">...</span><br></pre></td></tr></table></figure><hr><h2 id="系统耗时分析方案"><a href="#系统耗时分析方案" class="headerlink" title="系统耗时分析方案"></a>系统耗时分析方案</h2><p>系统做一些耗时分析的操作会有一些Log标志：</p><ol><li><code>binder_sample</code>：</li></ol><ul><li>功能说明: 监控每个进程的主线程的binder transaction的耗时情况, 当超过阈值时,则输出相应的目标调用信息，默认1000ms打开。</li><li>log格式: <code>52004 binder_sample (descriptor|3),(method_num|1|5),(time|1|3),(blocking_package|3),(sample_percent|1|6)</code></li><li>log实例:</li></ul><p><code>2754 2754 I binder_sample: [android.app.IActivityManager,35,2900,android.process.media,5]</code></p><p>从上面的log中可以得出:</p><ul><li>主线程2754;</li><li>执行android.app.IActivityManager接口</li><li>所对应方法code =35(即STOP_SERVICE_TRANSACTION),</li><li>所花费时间为2900ms</li></ul><p>该block所在package为 android.process.media，最后一个参数是sample比例(没有太大价值)</p><ol start="2"><li><code>dvm_lock_sample</code></li></ol><ul><li>功能说明: 当某个线程等待lock的时间blocked超过阈值,则输出当前的持锁状态 ;</li><li>log格式: <code>20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)</code></li></ul><p><code>进程名，主线程？线程名，锁等待时间，下个持有者文件名，行号，上个持有者文件名（如果和下个相同，则是-），行号，等待百分比</code></p><ul><li>log实例:</li></ul><p><code>dvm_lock_sample: [system_server,1,Binder_9,1500,ActivityManagerService.java,6403,-,1448,0]</code></p><p>意思是system_server: Binder_9,执行到ActivityManagerService.java的6403行代码,一直在等待AMS锁, 而该锁所同一文件的1448行代码所持有, 从而导致Binder_9线程被阻塞1500ms.</p><ol start="3"><li><code>binder starved</code></li></ol><ul><li>功能说明:当system_server等进程的线程池使用完, 无空闲线程时, 则binder通信都处于饥饿状态, 则饥饿状态超过一定阈值则输出信息;</li><li>云控参数: persist.sys.binder.starvation  (默认值16ms)</li><li>log实例:</li></ul><p><code>1232 1232 &quot;binder thread pool (16 threads) starved for 100 ms&quot;</code></p><p>解析: system_server进程的 线程池已满的持续长达100ms</p><hr><h2 id="kswapd0-CPU占用率很高"><a href="#kswapd0-CPU占用率很高" class="headerlink" title="kswapd0 CPU占用率很高"></a>kswapd0 CPU占用率很高</h2><p>如果出现kswapd0 cpu 占用率很高，可以先查看内存使用情况。</p><h3 id="proc-meminfo内存使用信息"><a href="#proc-meminfo内存使用信息" class="headerlink" title="/proc/meminfo内存使用信息"></a>/proc/meminfo内存使用信息</h3><p>例如以下，可用内存只有62MB。</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></pre></td><td class="code"><pre><span class="line">------ MEMORY INFO (&#x2F;proc&#x2F;meminfo) ------</span><br><span class="line">&#x2F;&#x2F;所有可用RAM大小 （即物理内存减去一些预留位和内核的二进制代码大小）</span><br><span class="line">&#x2F;&#x2F;可以认为是系统可供分配的内存总大小, 通常大小会比实际物理内存小</span><br><span class="line">MemTotal:        3844700 kB</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;LowFree与HighFree的总和</span><br><span class="line">&#x2F;&#x2F;当前系统空闲的内存大小，对应所有处于NR_FREE_PAGES状态的页框</span><br><span class="line">MemFree:           62100 kB</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;MemFree + Active(file) + Inactive(file) + SReclaimable 此外还考虑了内存压力水位(watermark)的情况，计算比较复杂，详细见 si_mem_available(). 这只是理论上系统可用的内存，即理论上可回收的内存，但是实际上能用的达不到这么多</span><br><span class="line">MemAvailable:     396584 kB</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;用来给块设备做的缓冲大小（只记录文件系统的metadata以及 tracking in-flight pages，就是说 buffers是用来存储，目录里面有什么内容，权限等等。）</span><br><span class="line">Buffers:           10160 kB</span><br><span class="line">&#x2F;&#x2F;用来给文件做缓冲大小（直接用来记忆我们打开的文件）. 它不包括SwapCached</span><br><span class="line">Cached:           554704 kB</span><br><span class="line">&#x2F;&#x2F;已经被交换出来的内存，但仍然被存放在swapfile中。用来在需要的时候很快的被替换而不需要再次打开I&#x2F;O端口</span><br><span class="line">SwapCached:        25312 kB</span><br><span class="line">&#x2F;&#x2F;最近经常被使用的内存，除非非常必要否则不会被移作他用</span><br><span class="line">Active:          1317228 kB</span><br><span class="line">&#x2F;&#x2F;最近不经常被使用的内存，非常用可能被用于其他途径</span><br><span class="line">Inactive:         659868 kB</span><br><span class="line">Active(anon):    1179772 kB</span><br><span class="line">Inactive(anon):   403532 kB</span><br><span class="line">Active(file):     137456 kB</span><br><span class="line">Inactive(file):   256336 kB</span><br><span class="line">Unevictable:      162512 kB</span><br><span class="line">Mlocked:          162512 kB</span><br><span class="line">&#x2F;&#x2F;交换空间的总和</span><br><span class="line">SwapTotal:       2113488 kB</span><br><span class="line">&#x2F;&#x2F;从RAM中被替换出暂时存在磁盘上的空间大小</span><br><span class="line">SwapFree:         405628 kB</span><br><span class="line">&#x2F;&#x2F;等待被写回到磁盘的内存大小</span><br><span class="line">Dirty:              1652 kB</span><br><span class="line">&#x2F;&#x2F;正在被写回到磁盘的内存大小</span><br><span class="line">Writeback:            20 kB</span><br><span class="line">AnonPages:       1571688 kB</span><br><span class="line">&#x2F;&#x2F;影射文件的大小</span><br><span class="line">Mapped:           340044 kB</span><br><span class="line">Shmem:              9360 kB</span><br><span class="line">&#x2F;&#x2F;内核数据结构缓存</span><br><span class="line">Slab:             233948 kB</span><br><span class="line">SReclaimable:      66952 kB</span><br><span class="line">SUnreclaim:       166996 kB</span><br><span class="line">KernelStack:       72480 kB</span><br><span class="line">PageTables:       109660 kB</span><br><span class="line">NFS_Unstable:          0 kB</span><br><span class="line">Bounce:                0 kB</span><br><span class="line">WritebackTmp:          0 kB</span><br><span class="line">CommitLimit:     4035836 kB</span><br><span class="line">Committed_AS:   125397000 kB</span><br><span class="line">&#x2F;&#x2F;vmalloc内存大小</span><br><span class="line">VmallocTotal:   263061440 kB</span><br><span class="line">&#x2F;&#x2F;已经被使用的虚拟内存大小</span><br><span class="line">VmallocUsed:           0 kB</span><br><span class="line">VmallocChunk:          0 kB</span><br><span class="line">CmaTotal:         344064 kB</span><br><span class="line">CmaFree:            2736 kB</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h3 id="dumpsys-meminfo"><a href="#dumpsys-meminfo" class="headerlink" title="dumpsys meminfo"></a>dumpsys meminfo</h3><p>直接dumpsys meminfo，是查看整体的内存占用情况，具体的还是需要加上process name。</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></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;对应上面MemTotal</span><br><span class="line">Total RAM: 3,844,700K (status normal)</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;cached pss对应变量cachedPss的值。这部分进程占用的内存并没有被释放，而由于他们都已切换到后台，且adj较低，系统认为可以释放掉这部分内存。所以对于这部分进程，系统最好有机制能及时清理掉从而释放内存。</span><br><span class="line">&#x2F;&#x2F;cached kernel对应&quot;Buffers+Cached+SReclaimable-Mapped&quot;这部分的内存由于理论上是可以被Kernel回收的，所以这里也计算在free中，但是这是一个理论上的值，实际上很难做到全部回收。</span><br><span class="line">&#x2F;&#x2F;free对应MemFree</span><br><span class="line"> Free RAM: 1,003,024K (  400,796K cached pss +   509,892K cached kernel +     1,136K cached ion +    91,200K free)</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;kernel对应&quot;Shmem+SUnreclaim+VmallocUsed+PageTables+KernelStack&quot;,其中VmallocUsed是统计&#x2F;proc&#x2F;vmallocinfo中除ioremap,map_lowmem,vm_map_ram之外的和</span><br><span class="line">&#x2F;&#x2F;详细见&quot;Debug.get_allocated_vmalloc_memory()&quot;这部分即是对kernel的内存占用的一个统计，如果要统计kernel的内存占用，这个稍微准确一些</span><br><span class="line"> Used RAM: 4,340,042K (3,862,614K used pss +   477,428K kernel)</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;对应MemTotal - (totalPss - totalSwapPss) - MemFree - (cached kernel) - (kernel) - zramtotal</span><br><span class="line"> Lost RAM:   135,889K</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;第一个数 用变量zramtotal来代替，表示zram实际占用的物理内存，是从&#x2F;sys&#x2F;block&#x2F;zram0&#x2F;mm_stat中统计而来</span><br><span class="line">&#x2F;&#x2F;第二个数 对应 SwapTotal - SwapFree , 是已经在swap区的内存大小</span><br><span class="line">&#x2F;&#x2F;第三个数 对应 SwapTotal， 是整个swap区的大小</span><br><span class="line">     ZRAM:   528,592K physical used for 1,757,284K in swap (2,113,488K total swap)</span><br><span class="line">   Tuning: 384 (large 512), oom   645,120K, restore limit   107,520K (high-end-gfx)</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.imooc.com/article/280763" target="_blank" rel="noopener">应用与系统稳定性第一篇—ANR问题分析的一般套路</a></li><li><a href="https://www.jianshu.com/p/30c1a5ad63a3" target="_blank" rel="noopener">Android应用ANR分析</a></li><li><a href="https://blog.csdn.net/shift_wwx/article/details/42490863" target="_blank" rel="noopener">android 查看内存使用情况</a></li><li><a href="https://zhuanlan.zhihu.com/p/90076117" target="_blank" rel="noopener">Android内存占用分析</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;ANR（Application Not Responding），字面意思是应用无响应，即用户的一些操作无法从应用中获取反馈。关于发生ANR的trace.txt文件的请参考&lt;a href=&quot;https://wizzie.top/Blog/2020/06/11/2020/200611_android_tracetxt/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Android ANR traces.txt文件分析&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android 图形显示框架</title>
    <link href="https://alonealive.github.io/Blog/2020/07/30/2020/200730_android_GraphicsFramework/"/>
    <id>https://alonealive.github.io/Blog/2020/07/30/2020/200730_android_GraphicsFramework/</id>
    <published>2020-07-30T13:52:00.000Z</published>
    <updated>2021-06-10T14:07:09.649Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本篇对Android图形显示框架做一个概述，内容主要包含：SurfaceSession创建和销毁（添加/删除窗口），Surface创建和销毁，BufferQueue创建，以及BufferQueue的dequeueBuffer和queueBuffer、acquire和release大致流程梳理。</p></blockquote><a id="more"></a><h2 id="显示框架概述"><a href="#显示框架概述" class="headerlink" title="显示框架概述"></a>显示框架概述</h2><p>Android与用户进行图形界面的交互，例如各个应用程序，他们的对话框、按钮、菜单等图形窗口。这些窗口的管理都是由WindowManager负责，窗口管理位于Java层，真正的实现者是运行在System_server进程空间中的WindowManagerService。</p><figure class="highlight java"><figcaption><span>frameworks/base/services/java/com/android/server/SystemServer.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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"> * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.</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">startOtherServices</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    .......</span><br><span class="line">        traceBeginAndSlog(<span class="string">"StartWindowManagerService"</span>);</span><br><span class="line">        <span class="comment">// WMS needs sensor service ready</span></span><br><span class="line">        ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);</span><br><span class="line">        mSensorServiceStart = <span class="keyword">null</span>;</span><br><span class="line">        wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,</span><br><span class="line">                <span class="keyword">new</span> PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);</span><br><span class="line">        ServiceManager.addService(Context.WINDOW_SERVICE, wm, <span class="comment">/* allowIsolated= */</span> <span class="keyword">false</span>,</span><br><span class="line">                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);</span><br><span class="line">        ServiceManager.addService(Context.INPUT_SERVICE, inputManager,</span><br><span class="line">                <span class="comment">/* allowIsolated= */</span> <span class="keyword">false</span>, DUMP_FLAG_PRIORITY_CRITICAL);</span><br><span class="line">        traceEnd();</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>应用程序负责修改绘制窗口中的内容，而WindowManager负责窗口的生命周期、几何属性、坐标变换信息、用户输入焦点、动画等功能。他还管理着窗口状态的变化，如窗口位置、大小、透明度以及Z-order（前后遮盖顺序）等一系列的逻辑判断。这些WindowManager功能由一系列接口或类构成，包括ViewManager、WindowManager、WindowManagerImpl、WindowManagerService等。</p><p>SurfaceFlinger负责分配应用程序所需的图形缓冲区，并对系统中的整个图形窗口进行composition（合成）。</p><p>最终，图形窗口会更新显示到Display显示器上。</p><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-0-%E6%A6%82%E8%BF%B0.png" alt="显示框架概述"></p><hr><h3 id="显示过程的三个进程"><a href="#显示过程的三个进程" class="headerlink" title="显示过程的三个进程"></a>显示过程的三个进程</h3><p>Android显示的整个过程由App进程、System_server进程、SurfaceFlinger进程一起配合完成。</p><ol><li><p>App进程： App需要将自己的内容显示在屏幕上，所以需要负责发起Surface创建的请求。同时触发对控件的测量、布局、绘制以及输入事件的派发处理，这些主要在ViewRootImpl中触发；</p></li><li><p>System_server进程： 主要是WindowManagerService，负责接收App请求，同时和SurfaceFlinger建立连接，向SurfaceFlinger发起具体请求创建Surface，并且创建Surace的辅助管理类SurfaceControl（和window一一对应）(AMS作用是统一调度所有App的Activity)；</p></li><li><p>SurfaceFlinger： 为App创建具体的Surface，在SurfaceFLinger对应成Layer，然后负责管理、合成所有图层，最终显示。</p></li></ol><p><img src="Android_GraphicsProcess.png" alt="显示过程的三个进程"></p><hr><h3 id="Activity、Window、PhoneWindow、DecorView、View的对应关系"><a href="#Activity、Window、PhoneWindow、DecorView、View的对应关系" class="headerlink" title="Activity、Window、PhoneWindow、DecorView、View的对应关系"></a>Activity、Window、PhoneWindow、DecorView、View的对应关系</h3><ol><li>Window：每一个Activity都包含一个Window对象（抽象类，提供了绘制窗口的一组通用API），通常由PhoneWindow实现。</li></ol><p>在Activity.java中定义：<code>private Window mWindow;</code></p><ul><li>一个Activity对应创建一个Surface</li></ul><ol start="2"><li>PhoneWindow:继承于Window，是Window类的具体实现。该类内部包含了一个DecorView对象，该DecorView对象是所有应用窗口（Activity界面）的根View。</li></ol><p>简而言之，PhoneWindow类是把一个FrameLayout类，即DecorView对象进行一定的包装，将他作为应用窗口的根View，并提供一组通用的窗口操作接口。</p><p>PhoneWindow是Android中最基本的窗口系统，每个Activity都会创建一个PhoneWindow对象，是Activity和整个View系统交互的接口。</p><p>在Activity.java的attach函数实例化：<code>mWindow = new PhoneWindow(this, window, activityConfigCallback);</code></p><ol start="3"><li>DecorView：PhoneWindow构造函数中定义，继承FrameLayout类，是所有应用窗口的根View。</li></ol><p>在PhoneWindow.java中定义，构造函数中初始化：<code>private DecorView mDecor;</code></p><p><strong>相关debug方法：</strong></p><ul><li>adb shell dumpsys activity</li><li>adb shell dumpsys window</li></ul><p>![window包含关系]](phonewindow.png)</p><hr><h3 id="Activity生命周期"><a href="#Activity生命周期" class="headerlink" title="Activity生命周期"></a>Activity生命周期</h3><blockquote><p>Activity onResume添加窗口</p></blockquote><p>onCreate方法中调用setContentView来设置布局，此时只是完成了View Tree的创建。<a href="https://wizzie.top/Blog/2020/07/07/2020/200707_android_HWUI_Draw/#%E7%BB%98%E5%88%B6%E5%BA%8F%E5%88%97%E5%9B%BE" target="_blank" rel="noopener">此处参考HWUI绘制文章</a></p><p>真正通知WMS添加窗口，是在回调onResume完成的。</p><p>调用onResume的方法在ActivityThread.java中是<code>handleResumeActivity</code>。之后调用到WMS.java的addWindow。</p><p><img src="ActivityLifecycle.png" alt="Activity生命周期"></p><hr><h4 id="App进程中完成添加窗口操作"><a href="#App进程中完成添加窗口操作" class="headerlink" title="App进程中完成添加窗口操作"></a>App进程中完成添加窗口操作</h4><ol><li>当一个新窗口(Window)被创建的时候，在ActivityThread.java的<code>handleResumeActivity</code>中调用addView(),然后调用到<code>WindowManagerImpl</code>的addView()函数。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/WindowManagerImpl.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">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><ol start="2"><li>这个函数将实际操作委托给mGlobal成员完成，这个成员随着WindowManagerImpl的创建而被初始化：<code>private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();</code></li></ol><p><strong>WindowManagerGlobal是一个单例模式，即一个进程中最多仅有一个WindowManagerGlobal实例。</strong></p><ol start="3"><li>调用mGlobal的addView函数后，将会创建一个ViewRootImpl对象，并且将窗口的控件、布局参数、ViewRootImpl对象入参到setView函数中，这个动作将导致<strong>ViewRootImpl向WMS添加新的窗口、申请Surface创建、绘制动作等</strong>。这才真正意义的完成了窗口的添加操作。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/WindowManagerGlobal.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">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">        root = <span class="keyword">new</span> ViewRootImpl(view.getContext(), display);</span><br><span class="line"></span><br><span class="line">        view.setLayoutParams(wparams);</span><br><span class="line"></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"></span><br><span class="line">        <span class="comment">// do this last because it fires off messages to start doing things</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            root.setView(view, wparams, panelParentView);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RuntimeException e) &#123;</span><br><span class="line">            <span class="comment">// BadTokenException or InvalidDisplayException, clean up.</span></span><br><span class="line">            <span class="keyword">if</span> (index &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                removeViewLocked(index, <span class="keyword">true</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h4 id="窗口移除序列图（Activity-destroy）"><a href="#窗口移除序列图（Activity-destroy）" class="headerlink" title="窗口移除序列图（Activity destroy）"></a>窗口移除序列图（Activity destroy）</h4><p>窗口被删除，Activity执行了handleDestroyActivity函数：</p><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-1-3-Activity_Destroy.png" alt="Activity destroy"></p><h4 id="Surface-Destroy（Activity-pause或者stop状态）"><a href="#Surface-Destroy（Activity-pause或者stop状态）" class="headerlink" title="Surface Destroy（Activity pause或者stop状态）"></a>Surface Destroy（Activity pause或者stop状态）</h4><blockquote><p>可以结合以下<code>Surface创建部分</code>一起梳理，针对的情况是Surface被destroy，从framework/base到SurfaceFlinger模块Layer析构的流程。<br>但是就Activity而言，并没有被销毁，而是类似按了home键返回桌面，或者在后台运行的状态，具体可以通过日志观察。</p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-1-2-Surface_Destroy.png" alt="Surface Destroy"></p><hr><h2 id="SurfaceSession创建"><a href="#SurfaceSession创建" class="headerlink" title="SurfaceSession创建"></a>SurfaceSession创建</h2><blockquote><p>此处是接着上面添加窗口的流程，分为以下两部分。</p></blockquote><h3 id="mGlobal-addView中创建ViewRootImpl对象"><a href="#mGlobal-addView中创建ViewRootImpl对象" class="headerlink" title="mGlobal.addView中创建ViewRootImpl对象"></a>mGlobal.addView中创建ViewRootImpl对象</h3><ol><li>新建ViewRootImpl对象的时候，调用构造函数，会从WindowManagerGlobal中获取一个窗口session。</li></ol><p><code>mWindowSession = WindowManagerGlobal.getWindowSession();</code></p><ol start="2"><li>在WindowManagerGlobal中会通过Binder IPC跨进程创建一个session。</li></ol><p>Session主要用于进程间通信，其他应用程序想要和WMS通信就需要经过Session，每个应用程序进程都会对应一个Session，WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端。</p><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/WindowManagerGlobal.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">@UnsupportedAppUsage</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> IWindowSession <span class="title">getWindowSession</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (WindowManagerGlobal<span class="class">.<span class="keyword">class</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (sWindowSession == <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">// Emulate the legacy behavior.  The global instance of InputMethodManager</span></span><br><span class="line">                    <span class="comment">// was instantiated here.</span></span><br><span class="line">                    <span class="comment">// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage</span></span><br><span class="line">                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();</span><br><span class="line">                    <span class="comment">//获取WMS对象</span></span><br><span class="line">                    IWindowManager windowManager = getWindowManagerService();</span><br><span class="line">                    <span class="comment">//创建Session</span></span><br><span class="line">                    sWindowSession = windowManager.openSession(</span><br><span class="line">                            <span class="keyword">new</span> IWindowSessionCallback.Stub() &#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">onAnimatorScaleChanged</span><span class="params">(<span class="keyword">float</span> scale)</span> </span>&#123;</span><br><span class="line">                                    ValueAnimator.setDurationScale(scale);</span><br><span class="line">                                &#125;</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">                    <span class="keyword">throw</span> e.rethrowFromSystemServer();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> sWindowSession;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>WMS继承IWindowManager.Stub，调用到openSessio函数，创建一个新的session对象, 返回值是IWindowSession类型。用于在APP进程和WMS之间建立联系。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">H</span> <span class="keyword">extends</span> <span class="title">android</span>.<span class="title">os</span>.<span class="title">Handler</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> IWindowSession <span class="title">openSession</span><span class="params">(IWindowSessionCallback callback)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Session(<span class="keyword">this</span>, callback);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="mGlobal-addView中调用ViewRootImpl-setView"><a href="#mGlobal-addView中调用ViewRootImpl-setView" class="headerlink" title="mGlobal.addView中调用ViewRootImpl.setView"></a>mGlobal.addView中调用ViewRootImpl.setView</h3><p>在前面mGlobal创建ViewRootImpl对象之后，会调用ViewRootImpl对象的setView，然后通知到WMS创建一个SurfaceSession，建立WindowManagerService和Surfacelinger的连接。</p><p>一个SurfaceSession代表着一个到SurfaceFlinger的连接会话，在这个连接会话里，可以创建一个或多个surface，最后这些surface被合成送到Display上显示。</p><p>大致过程：（查看下面的序列图）</p><ol><li>在setView()中调用mWindowSession.addToDisplay, mWindowSession是IWindowSession接口类型，而Session.java实现了该接口；</li><li>Session.java 中调用mService.addWindow(…), mService是WMS类型；</li><li>WMS.java的addWindow()创建WindowState对象win，调用win.attach()</li><li>frameworks/base/services/core/java/com/android/server/wm/WindowState.java  调用attach</li><li>frameworks/base/services/core/java/com/android/server/wm/Session.java  调用windowAddedocked，创建SurfaceSession类型的mSurfaceSession</li><li>frameworks/base/core/java/android/view/SurfaceSession.java  构造函数调用JNI，然后在android_view_SurfaceSession.cpp中的nativeCreate创建SurfaceComposerClient, 调用Refase的incStrong然后实现onFirstRef,通过调用CreateConnection()建立和SF的连接;</li><li>SF.cpp  调用CreateConnection()返回SF的Client类的Binder代理BpSurfaceComposerClient;</li></ol><hr><h2 id="Surface创建"><a href="#Surface创建" class="headerlink" title="Surface创建"></a>Surface创建</h2><h3 id="App进程请求创建Surface"><a href="#App进程请求创建Surface" class="headerlink" title="App进程请求创建Surface"></a>App进程请求创建Surface</h3><p>Surface是Android图形系统的核心部分，图形界面上的一个窗口或对话框等都对应着一个Surface。</p><p>而这个Surface是一块绘制区域的抽象，它对应着Server服务端Surfacelinger中的一个图层Layer，这个图层的背后是一块图形缓冲区GraphicBuffer，Client客户端的应用程序的UI使用软件绘制、硬件绘制在Surface上各种渲染操作时，绘制操作的结果其实也就是在该图形缓冲区中。</p><p>这部分的内容是梳理Surface创建的过程。</p><ol><li>在ViewRootImpl对象中，<code>setView到requestLayout函数请求布局，到调用scheduleTraversals</code>，该函数里面在Choreographer.java层层调用到<code>Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);</code></li></ol><p>此处的action即是新的线程启动。</p><ol start="2"><li>启动ViewRootImp中单独的线程TraversalRunnable，然后调用到关键函数<code>performTraversals()</code>。</li></ol><p>这个关键函数有两个主要的函数:</p><ul><li>relayoutWindow()   -&gt;布局窗口</li><li>performDraw()    -&gt;绘制渲染</li></ul><p>请求创建Surface就从relayoutWindow函数开始。</p><p>在这个方法中调用IWindowSession的relayout，会调用到Session.java，然后调用到WMS的relayoutWindow从而达到跨进程：（流程图查看下面单独章节的序列图）</p><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/ViewRootImpl.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">relayoutWindow</span><span class="params">(WindowManager.LayoutParams params, <span class="keyword">int</span> viewVisibility,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">boolean</span> insetsPending)</span> <span class="keyword">throws</span> RemoteException </span>&#123;</span><br><span class="line">            ......</span><br><span class="line">    <span class="comment">//此处relayout会调用到WMS的relayoutWindow</span></span><br><span class="line">    <span class="keyword">int</span> relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,</span><br><span class="line">            (<span class="keyword">int</span>) (mView.getMeasuredWidth() * appScale + <span class="number">0.5f</span>),</span><br><span class="line">            (<span class="keyword">int</span>) (mView.getMeasuredHeight() * appScale + <span class="number">0.5f</span>), viewVisibility,</span><br><span class="line">            insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : <span class="number">0</span>, frameNumber,</span><br><span class="line">            mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,</span><br><span class="line">            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,</span><br><span class="line">            mPendingMergedConfiguration, mSurfaceControl, mTempInsets);</span><br><span class="line">    <span class="keyword">if</span> (mSurfaceControl.isValid()) &#123;</span><br><span class="line">        mSurface.copyFrom(mSurfaceControl);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        destroySurface();</span><br><span class="line">    &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><hr><h3 id="System-server进程-——-WMS"><a href="#System-server进程-——-WMS" class="headerlink" title="System_server进程 —— WMS"></a>System_server进程 —— WMS</h3><ol><li>跨进程到WMS后，从relayoutWindow函数调用到<code>createSurfaceControl(outSurfaceControl)</code>。</li></ol><p>（1）然后先是在WindowStateAnimator创建一个WindowSurfaceController对象，作为调用到WindowStateAnimator.java的<code>createSurfaceLocked</code>函数的返回值。</p><p>在createSurfaceLocked函数中，会new一个WindowSurfaceController对象，从而调用他的构造函数。</p><p>在他的构造函数中会创建一个SurfaceControl内部类Builder对象，调用该对象的build函数；</p><p>(2) WMS.java中调用WindowStateAnimator.java的<code>createSurfaceLocked</code>函数之后，会执行以下逻辑：</p><p>a. 如果surfaceController不为空，调用WindowSurfaceController的getSurfaceControl；</p><p>b. WindowSurfaceController.java调用getSurfaceControl, <code>outSurfaceControl.copyFrom(mSurfaceControl);</code>，而mSurfaceControl就是之前的构造函数创建的。此处的copyFrom会经过JNI调用到Native层, 然后读取SurfaeControl。</p><ol start="2"><li><p>在Builder内部类的build函数中<code>创建Java层的SurfaceControl对象</code>，在SurfaceControl的构造函数中调用JNI层的nativeCreate函数；</p></li><li><p>android_view_SurfaceControl.cpp的nativeCreate函数会调用SurfaceComposerClient.cpp的<code>createSurfaceChecked</code>函数，<code>创建一个surface（实际上是SurfaceControl）</code>，然后将surface返回。</p></li></ol><p><img src="WMS_CreateSurfaceControl.png" alt="创建Surface之Java层和Native层联系"></p><hr><h3 id="SurfaceFlinger进程"><a href="#SurfaceFlinger进程" class="headerlink" title="SurfaceFlinger进程"></a>SurfaceFlinger进程</h3><p>SurfaceComposerClinet.cpp位于frameworks/native/libs/gui模块。而<code>libgui库主要被JNI层中的代码调用，从而和Surfacelinger进程进行交互</code>，可以看做是Java层的Bn端，是SurfaceFlinger的Bp端。</p><p>比如此处的SurfaceComposerClinet通过Binder IPC（ISurfaceComposerClinet.cpp），跨进程到SurfaceFlinger进程。</p><ol><li>SurfaceComposerClinet作为Bp客户端调用：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/native/libs/gui/SurfaceComposerClient.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">SurfaceComposerClient::createSurfaceChecked</span><span class="params">(<span class="keyword">const</span> String8&amp; name, <span class="keyword">uint32_t</span> w, <span class="keyword">uint32_t</span> h,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                     PixelFormat format,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                     sp&lt;SurfaceControl&gt;* outSurface, <span class="keyword">uint32_t</span> flags,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                     SurfaceControl* parent,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                     LayerMetadata metadata)</span> </span>&#123;</span><br><span class="line">    sp&lt;SurfaceControl&gt; sur;</span><br><span class="line">    <span class="keyword">status_t</span> err = mStatus;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (mStatus == NO_ERROR) &#123;</span><br><span class="line">        sp&lt;IBinder&gt; handle;</span><br><span class="line">        sp&lt;IBinder&gt; parentHandle;</span><br><span class="line">        sp&lt;IGraphicBufferProducer&gt; gbp;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (parent != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">            parentHandle = parent-&gt;getHandle();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        err = mClient-&gt;createSurface(name, w, h, format, flags, parentHandle, <span class="built_in">std</span>::move(metadata),</span><br><span class="line">                                     &amp;handle, &amp;gbp);</span><br><span class="line">        ALOGE_IF(err, <span class="string">"SurfaceComposerClient::createSurface error %s"</span>, strerror(-err));</span><br><span class="line">        <span class="keyword">if</span> (err == NO_ERROR) &#123;</span><br><span class="line">            *outSurface = <span class="keyword">new</span> SurfaceControl(<span class="keyword">this</span>, handle, gbp, <span class="literal">true</span> <span class="comment">/* owned */</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> err;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>Bn服务端是surfaceflinger模块的Client.cpp，此时跨进程到SurfaceFlinger进程，调用createSurface，从而请求到SurfaceFlinger创建Surface：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/native/services/surfaceflinger/Client.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">status_t</span> <span class="title">Client::createSurface</span><span class="params">(<span class="keyword">const</span> String8&amp; name, <span class="keyword">uint32_t</span> w, <span class="keyword">uint32_t</span> h, PixelFormat format,</span></span></span><br><span class="line"><span class="function"><span class="params">                               <span class="keyword">uint32_t</span> flags, <span class="keyword">const</span> sp&lt;IBinder&gt;&amp; parentHandle,</span></span></span><br><span class="line"><span class="function"><span class="params">                               LayerMetadata metadata, sp&lt;IBinder&gt;* handle,</span></span></span><br><span class="line"><span class="function"><span class="params">                               sp&lt;IGraphicBufferProducer&gt;* gbp)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// We rely on createLayer to check permissions.</span></span><br><span class="line">    <span class="keyword">return</span> mFlinger-&gt;createLayer(name, <span class="keyword">this</span>, w, h, format, flags, <span class="built_in">std</span>::move(metadata), handle, gbp,</span><br><span class="line">                                 parentHandle);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>在SurfaceFlinger::createLayer中创建Layer（创建surface的请求到SurfaceFlinger进程中就是创建Layer），创建的Layer有四种：</li></ol><p>（1）createBufferQueueLayer<br>（2）createBufferStateLayer<br>（3）createColorLayer<br>（4）createContainerLayer</p><p>通常情况下创建的是第一种Layer——BufferQueueLayer（在P中是BufferLayer），会创建一个<code>&lt;sp&gt;BufferQueueLayer强指针对象</code>。</p><figure class="highlight cpp"><figcaption><span>SF.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">status_t</span> <span class="title">SurfaceFlinger::createBufferQueueLayer</span><span class="params">(<span class="keyword">const</span> sp&lt;Client&gt;&amp; client, <span class="keyword">const</span> String8&amp; name,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                <span class="keyword">uint32_t</span> w, <span class="keyword">uint32_t</span> h, <span class="keyword">uint32_t</span> flags,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                LayerMetadata metadata, PixelFormat&amp; format,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                sp&lt;IBinder&gt;* handle,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                sp&lt;IGraphicBufferProducer&gt;* gbp,</span></span></span><br><span class="line"><span class="function"><span class="params">                                                sp&lt;Layer&gt;* outLayer)</span> </span>&#123;</span><br><span class="line">.....</span><br><span class="line">    sp&lt;BufferQueueLayer&gt; layer = getFactory().createBufferQueueLayer(</span><br><span class="line">            LayerCreationArgs(<span class="keyword">this</span>, client, name, w, h, flags, <span class="built_in">std</span>::move(metadata)));</span><br><span class="line">    <span class="keyword">status_t</span> err = layer-&gt;setDefaultBufferProperties(w, h, format);</span><br><span class="line">    <span class="keyword">if</span> (err == NO_ERROR) &#123;</span><br><span class="line">        *handle = layer-&gt;getHandle();</span><br><span class="line">        *gbp = layer-&gt;getProducer();</span><br><span class="line">        *outLayer = layer;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ALOGE_IF(err, <span class="string">"createBufferQueueLayer() failed (%s)"</span>, strerror(-err));</span><br><span class="line">                                                &#125;</span><br></pre></td></tr></table></figure><hr><h2 id="序列图"><a href="#序列图" class="headerlink" title="序列图"></a>序列图</h2><blockquote><p>该序列图包含上面部分的流程，包含APP进程和WMS进程之间的Session创建、SurfaceSession创建、<strong>Surface创建</strong>。</p></blockquote><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-1-1-Surface%E5%88%9B%E5%BB%BA.png" alt="Surface创建"></p><hr><h2 id="BufferQueue"><a href="#BufferQueue" class="headerlink" title="BufferQueue"></a>BufferQueue</h2><blockquote><p>关于BufferQueue只大致梳理他的创建流程，以及在渲染过程中的dequeuebuffer和queuebuffer流程、在合成过程中的acquire和release流程。关于和GraphicsBuffer和再底层的逻辑，暂时不梳理。</p></blockquote><h3 id="BufferQueue概述"><a href="#BufferQueue概述" class="headerlink" title="BufferQueue概述"></a>BufferQueue概述</h3><p>创建BuffeQueueLayer对象的onFirstRef中会创建一个BufferQueue。BufferQueue是buffer流转的中转站。具体分成四个步骤：</p><ol><li>生产者dequeue一块buffer，buffer状态-&gt;DEQUEUED，持有者-&gt;Producer，之后生产者可以填充数据（渲染绘制）。在dequeueBuffer之前，buffer状态是free，持有者是BufferQueue；</li><li>生产者填充完数据后，进行queue操作，buffer-&gt;QUEUED，持有者-&gt;BufferQueue。操作后producer会回调BufferQueue的onFrameAvailable函数，通知消费者有可用的buffer;</li><li>消费者进行acquire取出Buffer，buffer-&gt;ACQUIRED，持有者-&gt;Consumer;</li><li>消费者消费完这块buffer（已经合成），进行release操作释放，归还给BufferQueue</li></ol><p><img src="BufferQueue.png" alt="BufferQueue处理流程"></p><hr><h3 id="BufferQueue状态"><a href="#BufferQueue状态" class="headerlink" title="BufferQueue状态"></a>BufferQueue状态</h3><ul><li>DEQUEUED 状态：</li></ul><p>Producer dequeue一个Buffer后，这个Buffer就变为DEQUEUED状态，release Fence发信号后，Producer就可以修改Buffer的内容，我们称为release Fence。此时Buffer被Producer占用。</p><p>DEQUEUED状态的Buffer可以迁移到 QUEUED 状态，通过queueBuffer或attachBuffer流程。也可以迁移到FREE装，通过cancelBuffer或detachBuffer流程。</p><ul><li>QUEUED 状态：</li></ul><p>Buffer绘制完后，queue到BufferQueue中，给Consumer进行消费。此时Buffer可能还没有真正绘制完成，必现要等对应的Fence发信号出来后，才真正完成。此时Buffer是BufferQueue持有，可以迁移到ACQUIRED状态，通过acquireBuffer流程。而且可以迁移到FREE状态，如果另外一个Buffer被异步的queue进来。</p><ul><li>ACQUIRED 状态：</li></ul><p>Buffer已经被Consumer获取，但是也必须要等对应的Fence发信号才能被Consumer读写，找个Fence是从Producer那边，queueBuffer的时候传过来的。我们将其称为acquire fence。此时，Buffer被Consumer持有。状态可以迁移到FREE状态，通过releaseBuffer或detachBuffer流程。除了从acquireBuffer流程可以迁移到ACQUIRED状态，attachBuffer流程也可以迁移到ACQUIRED状态。</p><ul><li>FREE 状态：</li></ul><p>FREE状态，说明Buffer被BufferQueue持有，可以被Producer dequeue，它将迁移到DEQUEUED状态，通过dequeueBuffer流程。</p><ul><li>SHARED状态：</li></ul><p>SHARED状态是一个特殊的状态，SHARED的Buffer并不参与前面所说的状态迁移。它说明Buffer被用与共享Buffer模式。除了FREE状态，它可以是其他的任何状态。它可以被多次dequeued, queued, 或者 acquired。这中共享Buffer的模式，主要用于VR等低延迟要求的场合。</p><hr><h3 id="BufferQueue创建以及创建一个监听"><a href="#BufferQueue创建以及创建一个监听" class="headerlink" title="BufferQueue创建以及创建一个监听"></a>BufferQueue创建以及创建一个监听</h3><p>在<code>BufferQueueLayer::onFirstRef</code>调用BufferQueue::createBufferQueue()创建了bufferQueue、生产者、消费者，在创建生产者和消费者的过程中，将他们绑定到同一个BufferQueue上。</p><p>之后会创建一个BufferLayerConsumer对象mConsumer，这个对象继承了ConsumerBase类，所以会回调基类的构造函数，注册一个监听对象到BufferQueue（空对象）。</p><p>真正的监听是在<code>mConsumer-&gt;setContentsChangedListener(this)</code>基类构造函数中还会调用consumerConnect将消费者关联到BufferQueue中。此时监听对象就赋给了BufferQueue的mConsumerListener成员（调用BufferQueueConsumer的connect函数）。</p><p>这个监听对象会在queueBuffer是触发，由生产者回调注册到BufferQueue的帧可用通知。</p><p><img src="BufferQueue_Create.png" alt="BufferQueue创建"></p><hr><h3 id="DequeuBuffer"><a href="#DequeuBuffer" class="headerlink" title="DequeuBuffer"></a>DequeuBuffer</h3><p>BufferQueue创建后，首先由生产者执行dequeueBuffer请求一块Buffer。</p><p>Dequeue和Queue的操作都是在硬件渲染(软件绘制暂不考虑)的过程中，在<code>ThreadedRenderer::draw</code>函数中，updateRootDisplayList创建好一个RootDisplayList后，开始渲染一帧，在这时调用父类的syncAndDrawFrame函数，这个函数层层调到CanvasContext::draw函数，然后依次调用三个函数：</p><ol><li>mRenderPipeline-&gt;getFrame            执行dequeueBuffer请求一块buffer</li><li>draw                                 填充buffer</li><li>mRenderPipeline-&gt;swapBuffers         执行queueBuffer送到BufferQueue</li></ol><p>在此处dequeuBuffer和queueBuffer两个操作调到gui/Surface.cpp的两个对应函数，这个流程基本一样。</p><p>大致都从<code>SkiaOpenPipeline.cpp -&gt; EglManager.cpp -&gt; eglApi.cpp -&gt; ANativeWindow.cpp</code>，之后到Bp客户端libgui库的Surface.cpp，执行具体操作。</p><p>Surface::dequeueBuffer中调用IGraphicBufferProducer::dequeueBuffer。然后<code>remote()-&gt;transact(DEQUEUE_BUFFER,data,&amp;reply)</code>调用到Bn端BufferQueueProducer::dequeueBuffer函数。</p><ol><li>首先查找mSlots[found]的序列号found，mSlots是存放Buffer的地方，他的数量是64。即至多存放64个buffer。</li><li>found是从waitForFreeSlotThenRelock中获取：</li></ol><ul><li>从非Free的buffer中统计dequeue和acquire的数量；</li><li>判断dequeueBufferCount数量不能大于最大允许dequeueBuffer的数量；</li><li>slot的获取主要来自两个集合，mFreeSlots和mFreeBuffers；两者包含的所有状态都是free，区别在于前者没有attached，后者以及attached；如果调用来自dequeueBuffer优先选择前者，如果调用来自attachBuffer，优先选择后者；</li><li>如果没找到符合要求的buffer或者queue的buffer还有太多没有完成，就要等待这个buffer被acquired或者released，或者修改最大的buffer数量。</li></ul><ol start="3"><li>找到可用的slot，还要判断是否重新分配空间：如果Buffer（本质上是GraphicBuffer）是空，并且需要重新分配空间，则对这个mSlots[found]初始化；</li><li>new GraphicBuffer为mSlots分配一个GraphicBuffer，赋值给BufferQueueCore中的变量mSlots[]的mGraphicBuffer；</li></ol><p><code>mSlots[*outSlot].mGraphicBuffer = graphicBuffer;</code></p><p><code>Surface::dequeueBuffer</code>从服务端申请到Buffer后，通过<code>requestBuffer</code>将客户端的buffer和服务端的buffer指向同一块物理内存。</p><p>具体是IGraphicBufferPruducer代理中通过REQUEST_BUFFER状态，在onTransact中将申请的GraphicBuffer，即<code>mSlots[slot].mGraphicBuffer</code>。将其写入reply，等待客户端读取。</p><hr><h3 id="QueueBuffer"><a href="#QueueBuffer" class="headerlink" title="QueueBuffer"></a>QueueBuffer</h3><p>queueBuffer是在渲染一帧后通过<code>mRenderPipeline-&gt;swapBuffers</code>调用到Surface::queueBuffer。将填充完数据的buffer放入BufferQueue，并且通过监听者通知消费者对象开始消费。</p><p>在Bn端BufferQueueProducer::queueBuffer L977中调用：<code>frameAvailableListener-&gt;onFrameAvailable(item);</code></p><p>通知消费者，在BufferQueueLayer::onFrameAvailable中调用：<code>mFlinger-&gt;signalLayerUpdate();</code></p><p>触发SurfaceFlinger的消息循环机制，开始处理SurfaceFlinger合成事件。</p><hr><h3 id="序列图-1"><a href="#序列图-1" class="headerlink" title="序列图"></a>序列图</h3><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-2-%E6%B8%B2%E6%9F%93%E6%97%B6BufferQueue-dequeue-queue.png" alt="渲染时BufferQueue的dequeue和queue操作"></p><h3 id="acquire-amp-release"><a href="#acquire-amp-release" class="headerlink" title="acquire &amp; release"></a>acquire &amp; release</h3><p>消费者SurfaceFlinger通过acquire从BufferQueue取出一块buffer消费。消费（合成）之后释放。</p><h3 id="序列图-2"><a href="#序列图-2" class="headerlink" title="序列图"></a>序列图</h3><p><img src="Android%E6%98%BE%E7%A4%BA%E6%A1%86%E6%9E%B6-3-BufferQueue-acquire-release.png" alt="合成时BufferQueue的acquire和release"></p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;本篇对Android图形显示框架做一个概述，内容主要包含：SurfaceSession创建和销毁（添加/删除窗口），Surface创建和销毁，BufferQueue创建，以及BufferQueue的dequeueBuffer和queueBuffer、acquire和release大致流程梳理。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android NE分析（二）</title>
    <link href="https://alonealive.github.io/Blog/2020/07/27/2020/200727_android_NE_Two/"/>
    <id>https://alonealive.github.io/Blog/2020/07/27/2020/200727_android_NE_Two/</id>
    <published>2020-07-27T13:52:00.000Z</published>
    <updated>2021-05-31T12:52:03.454Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>了解gcc将<code>*.c/cpp</code>编译成<code>*.o</code>，再将其链接为可执行程序或/lib库的过程，有助于我们将native从编译/加载/执行到崩溃一条路贯通起来。Android的Makefile只需要将source file填入<code>LOCAL_SRC_FILES</code>，然后<code>include $(BUILD_SHARED_LIBRARY)</code>或<code>$(BUILD_EXECUTABLE)</code>就可以将<code>*.c/cpp/s</code>编译为动态库或可执行程序。</p></blockquote><a id="more"></a><h2 id="native编译"><a href="#native编译" class="headerlink" title="native编译"></a>native编译</h2><h3 id="编译为obj"><a href="#编译为obj" class="headerlink" title="编译为obj"></a>编译为obj</h3><blockquote><p>在build/core/definitions.mk有定义transform-c-or-s-to-o-no-deps和transform-cpp-to-o，分别将每个<em>.c/s和</em>.cpp编译成*.o，里面传了很多参数给gcc</p></blockquote><ol><li><code>-fpic -fPIE</code></li></ol><ul><li>PIC是Position-Independent Code的缩写，经常被用在共享库中，这样就能将相同的库代码为每个程序映射到一个位置，不用担心覆盖掉其他程序或共享库。</li><li>PIE是Position-Independent-Executable的缩写，只能应用在可执行程序中。PIE和PIC很像，但做了一些调整（不用PLT，使用PC相关的重定位）。-fPIE给编译用，-pie给链接(ld)用。</li></ul><p>例如，一个程序没有使用PIC被链接到0地址，那么系统将其加载到0地址。</p><ol start="2"><li><code>-fstack-protector</code></li></ol><blockquote><p>顾名思义就是保护堆栈，每一个函数在运行时都有自己的栈帧，如果代码没有写好，很可能将自己甚至是其他的栈帧踩坏，那如何防护呢？简单的方法就是在栈帧头部也就是在局部变量开始之前多存储一个<strong>stack_chk_guard值，用于在函数返回前取出来和_stack_chk_guard做对比，失败则调用</strong>stack_chk_fail函数，这个就是该参数完成的行为。</p></blockquote><h3 id="静态链接"><a href="#静态链接" class="headerlink" title="静态链接"></a>静态链接</h3><p><code>build/core/combo/TARGET_linux-arm.mk</code>里有定义<code>transform-o-to-static-executable-inner</code>，将*.o链接成静态可执行程序，静态可执行程序是一个完整的程序，不需要额外的共享库即可执行，比如/init,/sbin/adbd等。</p><p>链接器用的是arm-linux-androideabi-g++</p><h3 id="动态链接"><a href="#动态链接" class="headerlink" title="动态链接"></a>动态链接</h3><p><code>build/core/combo/TARGET_linux-arm.mk</code>里有定义<code>transform-o-to-executable-inner</code>和<code>transform-o-to-shared-lib-inner</code>，分别将*.o链接为动态可执行程序和共享库。动态可执行程序需要linker才能进一步运行的。</p><p>链接器也是用arm-linux-androideabi-g++</p><h2 id="tombstone定位错误方法"><a href="#tombstone定位错误方法" class="headerlink" title="tombstone定位错误方法"></a>tombstone定位错误方法</h2><h3 id="signum"><a href="#signum" class="headerlink" title="signum"></a>signum</h3><p>一般debuggerd关注的是SIGILL，SIGBUS，SIGABRT，SIGFPE，SIGSEGV，SIGPIPE等。而这里，估计九成都是SIGSEGV (即signal 11)，段错误，和非法内存访问等。</p><h3 id="sigcode"><a href="#sigcode" class="headerlink" title="*sigcode"></a>*sigcode</h3><ul><li>SEGV_MAPERR：访问一个没有映射到任何内容的地址，这种情况通常就是野指针，或者越界访问，访问空指针也是属于这类</li><li>SEGV_ACCERR：试图访问您无权访问的地址。说明访问出错地址，被map到地址空间来了，但是没访问权限。基本上是指针越界或野指针，比如写只读map的内存地址</li></ul><p>例如：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7ecbf6a000   &#x2F;&#x2F;SEGV_ACCERR表示试图访问您无权访问的地址</span><br><span class="line">    x0  0000007e44001c30  x1  0000000000000000  x2  0000000000000000  x3  0000007e44001c30</span><br><span class="line">    x4  0000007ecbf6a008  x5  0000007e44001c98  x6  0000000000000000  x7  0000000000000000</span><br><span class="line">    x8  0000000000000000  x9  0000000000000000  x10 0000000000000000  x11 0000000000000000</span><br><span class="line">    x12 0000000000000000  x13 0000000000000000  x14 0000000000000001  x15 0000007ecbe3f540</span><br><span class="line">    x16 0000007eca917290  x17 0000007ec94ea480  x18 0000007e42e9c000  x19 0000007ecbdf5400</span><br><span class="line">    x20 0000000000000001  x21 0000007e44001c30  x22 0000000000000001  x23 0000007e44002020</span><br><span class="line">    x24 0000007ecb6bf045  x25 0000007ecb6bf260  x26 0000007ecb6bf278  x27 0000007ecb6bf041</span><br><span class="line">    x28 0000007ecb6bf044  x29 0000007e44001bf0</span><br><span class="line">    sp  0000007e44001bd0  lr  0000007eca910618  pc  0000007ec94ea460</span><br></pre></td></tr></table></figure><p>tombstone日志当中也提供了出错时寄存器地址里面的临近内存信息，信息量同样很丰富。查看<code>0000007e44001c30</code>附近的内存情况。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">memory near x0:</span><br><span class="line">    0000007e44001c10 0000000000030d40 00000000ffffffff  @...............</span><br><span class="line">    0000007e44001c20 0000007e44001cf0 0000007ecb6b83f0  ...D~.....k.~...</span><br><span class="line">    0000007e44001c30 0000000000000000 0000000000000000  ................</span><br><span class="line">    0000007e44001c40 0000000000000000 0000000000000000  ................</span><br><span class="line">    0000007e44001c50 0000000000000000 0000000000000000  ................</span><br><span class="line">    0000007e44001c60 0000000000000000 0000000000000000  ................</span><br><span class="line">    0000007e44001c70 0000000000000000 0000000000000000  ................</span><br><span class="line">    0000007e44001c80 0000000000000000 0000000000000000  ................</span><br><span class="line">    0000007e44001c90 0000000000000000 1c86a694ed72c72c  ........,.r.....</span><br><span class="line">    0000007e44001ca0 0000000000000001 00000000000fd000  ................</span><br><span class="line">    0000007e44001cb0 0000007ecb6b8170 0000007e44001d50  p.k.~...P..D~...</span><br><span class="line">    0000007e44001cc0 0000007e44001d50 0000007e44001dd8  P..D~......D~...</span><br><span class="line">    0000007e44001cd0 00000207000005ea 0000007e44001d50  ........P..D~...</span><br><span class="line">    0000007e44001ce0 0000007ec954e5f0 0000007e44001d50  ..T.~...P..D~...</span><br><span class="line">    0000007e44001cf0 0000007e44001d10 0000007ec954e618  ...D~.....T.~...</span><br><span class="line">    0000007e44001d00 0000007e44001d50 0000000000000000  P..D~...........</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li>Android Native/Tombstone Crash Log 详细分析：<a href="https://blog.csdn.net/u011006622/article/details/51496693" target="_blank" rel="noopener">https://blog.csdn.net/u011006622/article/details/51496693</a></li><li>Android Native程序crash的一些定位方法简介：<a href="https://msd.misuland.com/pd/300217191876268032" target="_blank" rel="noopener">https://msd.misuland.com/pd/300217191876268032</a></li><li>ARM64-memcpy.S 汇编源码分析：<a href="https://blog.csdn.net/ffmxnjm/article/details/68065090" target="_blank" rel="noopener">https://blog.csdn.net/ffmxnjm/article/details/68065090</a></li><li>android bionic memcpy 汇编源码解析：<a href="https://blog.csdn.net/qq_28637193/article/details/103681746" target="_blank" rel="noopener">https://blog.csdn.net/qq_28637193/article/details/103681746</a></li><li>PIC和PIE：<a href="https://www.cnblogs.com/sword03/p/9385660.html" target="_blank" rel="noopener">https://www.cnblogs.com/sword03/p/9385660.html</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;了解gcc将&lt;code&gt;*.c/cpp&lt;/code&gt;编译成&lt;code&gt;*.o&lt;/code&gt;，再将其链接为可执行程序或/lib库的过程，有助于我们将native从编译/加载/执行到崩溃一条路贯通起来。Android的Makefile只需要将source file填入&lt;code&gt;LOCAL_SRC_FILES&lt;/code&gt;，然后&lt;code&gt;include $(BUILD_SHARED_LIBRARY)&lt;/code&gt;或&lt;code&gt;$(BUILD_EXECUTABLE)&lt;/code&gt;就可以将&lt;code&gt;*.c/cpp/s&lt;/code&gt;编译为动态库或可执行程序。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android debug" scheme="https://alonealive.github.io/Blog/tags/android-debug/"/>
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
  </entry>
  
  <entry>
    <title>Android NE分析（一）</title>
    <link href="https://alonealive.github.io/Blog/2020/07/24/2020/200724_android_NE/"/>
    <id>https://alonealive.github.io/Blog/2020/07/24/2020/200724_android_NE/</id>
    <published>2020-07-24T12:52:00.000Z</published>
    <updated>2021-04-14T12:58:53.928Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>任何软件都可能存在BUG，调试和修复BUG伴随着整个开发流程，因此异常分析非常重要。如果是native层发生异常，我们一般称之为叫NE（native exception）</p></blockquote><a id="more"></a><p>异常可能发生任何一层，如果是：</p><ul><li>kernel层发生异常，叫KE（kernel exception）</li><li>native层发生异常，叫NE（native exception）</li><li>java成异常，叫JE（java exception）</li></ul><h2 id="NE简介"><a href="#NE简介" class="headerlink" title="NE简介"></a>NE简介</h2><blockquote><p>Native层是由各种lib/binary组成，这一层发生异常，我们称之为NE（native exception）<br>一般我们用offline调试，通过coredump借助gdb或trace32来调试。首先要先熟悉下linux信号和ptrace机制，coredump是通过信号触发生成的。<code>coredump是进程空间保存到文件系统的镜像，因此能看到异常时刻的所有变量值，就可以知道问题出在哪里。</code><br>Android是基于linux的，发生异常时，Android扩展了调试机制，这个机制是debuggerd机制。在没有coredump下，debuggerd以log或者tombstone的方式输出异常信息。</p></blockquote><p>本地应用程序是指可以直接运行在操作系统上，并且处理器直接执行机器码的程序。</p><p>在Android上，OS是linux，因此各种bin程序就是所谓的<code>natvie application</code>，比如/system/bin目录下的所有文件。</p><p>这些应用程序都是由GCC(c/c++)编译生成。</p><p>在Android软件架构里，这些应用程序组成了native layer:</p><p><img src="nativeLayer.png" alt="Native Layer"></p><p>native layer里的应用程序崩溃统称为Native Exception，即NE，比如空指针，非法指针，程序跑飞，内存踩坏等。</p><h3 id="发生NE流程"><a href="#发生NE流程" class="headerlink" title="发生NE流程"></a>发生NE流程</h3><p>原始的linux，对于用户进程崩溃之后，处理方式有2种：</p><ol><li>直接终止进程；</li><li>输出coredump再终止进程</li></ol><p>而在Android，为了方便调试，在收到崩溃信号后，会先输出tombstone，然后在根据设置是否抓取coredump，最后再终止进程。</p><p><strong>如果发生NE，内核会抛出信号，可以通过kernel log搜索<code>sig 11/7</code>等几个可以导致进程崩溃的关键字判断。</strong></p><p>此处的信号注册是发生在动态链接程序加载的时候，链接器（linker）负责将应用程序所需的库加载到进程空间内，然后跑应用程序大妈。linker在执行期间会注册信号。</p><p>流程：__linker_init() -&gt; __linker_init_post_relocation() -&gt; debuggerd_init()</p><h3 id="信号处理"><a href="#信号处理" class="headerlink" title="信号处理"></a>信号处理</h3><p>目前会产生native exception（NE）的几个信号需要特别掌握产生的原因，这样才能进一步分析问题所在。</p><p>内核发送信号过来后会执行<code>debuggerd_init()</code>里注册的函数debugger_signal_handler()，该函数会打印基本信息到main log：</p><p><code>logSignalSummary()</code>函数会输出基本异常信息，类似<code>libc: Fatal signal 11(SIGSEGV) at 0x000 ...</code>日志打印。</p><p>然后连接debuggerd的socket，将pid等信息发送给debuggerd，请它帮忙后续的处理。之后将对应的信号恢复为默认，等待debuggerd处理完后抓取coredump。</p><hr><h2 id="debuggerd服务"><a href="#debuggerd服务" class="headerlink" title="debuggerd服务"></a>debuggerd服务</h2><p>关于上面的debuggerd服务，是由init.rc启动起来，具体代码在<code>system/core/debuggerd/</code>。</p><p>debuggerd起来后会创建1个socket，然后监听，等待别人通过socket请求服务，服务可以是生成tombstone或调用栈。</p><h3 id="使用debuggerd命令查看指定tid的调用栈和tombstone"><a href="#使用debuggerd命令查看指定tid的调用栈和tombstone" class="headerlink" title="使用debuggerd命令查看指定tid的调用栈和tombstone"></a>使用debuggerd命令查看指定tid的调用栈和tombstone</h3><ul><li><code>adb shell debuggerd -b $tid</code>：抓取指定tid的调用栈</li><li><code>adb shell debuggerd $tid</code>：抓取指定tid的tombstone</li></ul><h3 id="ptrace-attach-detach"><a href="#ptrace-attach-detach" class="headerlink" title="ptrace attach/detach"></a>ptrace attach/detach</h3><p>通过socket拿到tid等资料后，使用ptrace attach上目标tid，之后就可以通过ptrace访问目标进程空间，然后打印一些NE相关的寄存器/调用栈等信息。</p><ol><li>ptrace attach：ptrace attach会发送<code>sig 19</code>给对应的进程。在这里，我们将进程内所有线程都attach上，防止有线程提前退出。</li><li>ptrace cont：attach之后还不能直接访问目标进程，因为目标进程还处于信号处理函数里面，我们需要让它恢复到异常现场，因此需要用ptrace cont让其继续执行。</li><li>waitpid：程序接着往下跑必然会再次发生异常（如果是SIGABRT,SIGFPE,SIGPIPE,SIGSTKFLT，则会在信号处理函数重发一次信号），kernel会再次发出信号，只不过由于进程被ptrace了，信号会送给debuggerd。<strong>到这里，目标进程已经收到2次同样的信号了</strong></li><li><code>tombstone</code>:debuggerd收到信号后，就可以生成tombstone了</li><li>ptrace detach: 完成工作后，需要detach ptrace，然后发送<code>sig 18</code>让其继续奔跑。如果是访问空指针等错误，程序会再次发生异常，由于在信号处理函数里已经将对应信号恢复默认，因此可能会产生coredump。</li></ol><hr><h3 id="产生db文件"><a href="#产生db文件" class="headerlink" title="产生db文件"></a>产生db文件</h3><blockquote><p>debuggerd完成之后会通知aee，aee就开始了打包db的工作，具体生成在<code>/data/aee_exp</code>目录</p></blockquote><p>1个完整的NE的db，里面除了coredump还有其他文件，包含log文件(main/event/radio)，详细的NE信息文件(_exp_main/_exp_detail)等，这些文件绝大部分是通过aee_dumpstate保存起来的。</p><h4 id="PROCESS-MAPS文件描述进程空间"><a href="#PROCESS-MAPS文件描述进程空间" class="headerlink" title="PROCESS_MAPS文件描述进程空间"></a>PROCESS_MAPS文件描述进程空间</h4><p>db中有些文件对分析NE是至关重要的，比如PROCESS_MAPS，这文件就是<code>/proc/$pid/maps</code>，里面是对进程空间的描述。</p><p>例如以下部分截取的内容：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;1-进程里地址范围            2-虚拟内存的权限（r&#x3D;读&#x2F;w&#x3D;写&#x2F;x&#x3D;可执行&#x2F;s&#x3D;共享&#x2F;p&#x3D;私有）</span><br><span class="line">&#x2F;&#x2F;3-空间所映射的文件的偏移量    4-空间所映射的文件的主次设备号</span><br><span class="line">&#x2F;&#x2F;5-空间所映射的文件的节点号（0表示没有节点映射到内存）   6-空间所映射的文件的路径</span><br><span class="line">7ec808a000-7ec808b000 --xp 00001000 fd:01 3989                           &#x2F;system&#x2F;lib64&#x2F;libhardware.so</span><br><span class="line">7ec808b000-7ec808c000 rw-p 00002000 fd:01 3989                           &#x2F;system&#x2F;lib64&#x2F;libhardware.so</span><br><span class="line">7ec808c000-7ec808d000 r--p 00003000 fd:01 3989                           &#x2F;system&#x2F;lib64&#x2F;libhardware.so</span><br></pre></td></tr></table></figure><hr><h2 id="Tombstone"><a href="#Tombstone" class="headerlink" title="Tombstone"></a>Tombstone</h2><blockquote><p>Android Native程序本质上就是一个Linux程序，因此当它在执行时发生严重错误，也会导致程序crash，然后产生一个记录crash的现场信息的文件，而这个文件在Android系统中就是tombstone文件。</p></blockquote><p>从上面的分析看，发生NE的时候会创建tombstone文件，最多存在10个，如果已存在10个，会覆盖最旧的文件。</p><p><strong>组成部分：</strong></p><ol><li>版本信息：主要是fingerprint，可以看出异常版本是eng还是user</li><li>寄存器信息：主要查看是哪个进程崩溃，信号是什么。寄存器信息需要配合下面的调用栈信息及数据信息结合GNU的工具（<code>objdump -S反汇编</code>）分析。</li><li>调用栈信息：是最直接可以看出异常的信息（可以使用addr2line定位地址的代码函数对应位置）</li><li>其他线程信息：如果异常线程和其他线程有逻辑关系的话，可以查看对应线程的信息</li><li>main log信息：全面的log建议还是查看main log</li></ol><h3 id="调用栈"><a href="#调用栈" class="headerlink" title="调用栈"></a>调用栈</h3><p>最直接查看栈的位置就是log和Tombstone文件中。</p><p>C/C++语言的过程调用都需要栈，正在执行的函数有属于自己的栈帧，函数内部的局部变量就放在栈帧里，当然还会存放函数的返回地址，这样函数执行结束之后才知道返回到哪里。</p><p>不同的栈帧关联在一起就会形成一个调用链，最顶端表示当前正在执行的函数，第2行表示调用它的函数，以此类推（先进后出）。</p><p>例如以下栈：</p><blockquote><p><code>库里的偏移</code>：库默认都加载在0地址的，由OS随机加载在mmap区域，因此实际上库函数的地址都是<code>基址+偏移量</code>，其中的基址就是库加载的地址，这个地址可以从<code>/proc/$pid/maps</code>看到每个库的基址。</p></blockquote><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></pre></td><td class="code"><pre><span class="line">backtrace:</span><br><span class="line">      #00 pc 00000000000e2f88  &#x2F;apex&#x2F;com.android.runtime&#x2F;lib64&#x2F;bionic&#x2F;libc.so (pthread_mutex_lock) (BuildId: ceedf0f98da575de138b0c631aceca44)</span><br><span class="line">    &#x2F;&#x2F;栈帧  +  地址       +       函数                                                 + 该函数的偏移量（+32） </span><br><span class="line">      #01 pc 00000000000c39a0  &#x2F;system&#x2F;lib64&#x2F;libgui.so (android::SurfaceControl::getHandle() const+32) (BuildId: d26a01ac90bf9cf5c7585e3fbbdbb327)</span><br><span class="line">      #02 pc 0000000000120a28  &#x2F;system&#x2F;lib64&#x2F;libandroid_runtime.so (android::nativeGetHandle(_JNIEnv*, _jclass*, long)+40) (BuildId: 5bd8af36fc54644383069436f63eec83)</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h2 id="addr2line命令"><a href="#addr2line命令" class="headerlink" title="addr2line命令"></a>addr2line命令</h2><p>针对发生Native Crash的堆栈，将地址转换为地址所在的文件及行数(显示所在函数)</p><p>该命令可以用来解析so动态链接库（Symbol目录），分析单个pc地址对应的函数，然后参照代码寻找问题发生处。</p><p>含有调试信息的库或程序必须和手机里的库或程序是同一次编译生成的，否则得到的PC值和库里的调试信息不匹配。</p><blockquote><p>如何确定库或程序含有调试信息呢？或者说如何确定库能不能用来调试呢？是不是symbol目录下的库呢？</p></blockquote><blockquote><p>用file命令即可查看：<code>file xxx</code></p></blockquote><ul><li>如果没有含debug信息的话，会显示：<code>xxx: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped</code></li><li>含有调试信息的话，则显示：<code>xxx: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, not stripped</code></li></ul><p>例如：（需要在Symbol目录下）</p><p><code>addr2line -Cfe .so address</code></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">&#x2F;out&#x2F;target&#x2F;product&#x2F;product&#x2F;symbols&#x2F;system&#x2F;lib64$ addr2line -f -e libgui.so 00000000000c39a0</span><br><span class="line">_ZNK7android12SortedVectorINS_12DisplayStateEE16do_move_backwardEPvPKvm</span><br></pre></td></tr></table></figure><p>解释：-e 后加上.so的文件名；-f 可同时输出函数名称</p><ul><li><code>-e --exe=&lt;executable&gt;</code>: 设置要查询地址的文件(默认: a.out)一般是<em>.so/</em>.a和可执行程序。此文件必须带有debug信息，在android codebase里是放在out/target/product/$project/symbols目录下</li><li><code>-f –functions</code>: 显示地址所在的函数名</li><li><code>-C --demangle[=style]</code>: 反重整函数名为可读方式</li></ul><h2 id="objdump"><a href="#objdump" class="headerlink" title="objdump"></a>objdump</h2><p><code>objdump –S **.so &gt; **.asm</code></p><p>该命令是用来把相应的so变成汇编语言的asm文件，然后根据地址信息就可以找到更加详细的相关函数信息。</p><p>即查看对象文件(<em>.so/</em>.a或应用程序)的内容信息</p><p>或者</p><p><code>symbols/out/target/product/project/symbols$ objdump -tT system/lib64/libgui.so</code></p><table><thead><tr><th align="center">后缀</th><th align="center">含义</th></tr></thead><tbody><tr><td align="center">-S 或 –source</td><td align="center">尽可能反汇编出源代码，尤其当编译的时候指定了-g这种调试参数时，效果比较明显。隐含了-d参数</td></tr><tr><td align="center">-t 或 –syms</td><td align="center">显示文件的符号表入口。类似于nm -s提供的信息</td></tr><tr><td align="center">-T 或 –dynamic-syms</td><td align="center">显示文件的动态符号表入口，仅仅对动态目标文件意义，比如某些共享库。它显示的信息类似于`nm -D</td></tr></tbody></table><h2 id="nm命令"><a href="#nm命令" class="headerlink" title="nm命令"></a>nm命令</h2><p>作用是：列出该文件的符号(函数，变量，文件等)，包含名字、地址、大小</p><hr><h2 id="ndk-stack"><a href="#ndk-stack" class="headerlink" title="ndk-stack"></a>ndk-stack</h2><blockquote><p>用来把log信息全部翻译成更加详细的带源码行数信息的log，相当于是在整个crash堆栈信息都执行addr2line命令。</p></blockquote><p>ndk-stack -sym  […/obj/local/{ABI类型}/]   -dump crash.log</p><hr><h2 id="readelf"><a href="#readelf" class="headerlink" title="readelf"></a>readelf</h2><p>查看elf文件(<em>.so/</em>.a或应用程序)的内容信息，可以使用<code>readelf -a [.so/.bin]</code>解析库地址。</p><hr><h2 id="c-filt"><a href="#c-filt" class="headerlink" title="c++filt"></a>c++filt</h2><p>反重整C++符号为可读方式。</p><p>根据解析结果查询函数，C++在linux系统编译后会变成类似<code>_ZNK...</code>的修饰名。使用<code>c++filt</code>获取函数的原始名称。</p><p><code>c++filt [_ZNK...函数修饰名]</code></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li>关于不同版本aee db文件的抓取：<a href="https://blog.csdn.net/topsecrethhh/article/details/81453414" target="_blank" rel="noopener">https://blog.csdn.net/topsecrethhh/article/details/81453414</a></li><li>coredump配置、产生、分析以及分析示例：<a href="https://www.cnblogs.com/arnoldlu/p/11160510.html" target="_blank" rel="noopener">https://www.cnblogs.com/arnoldlu/p/11160510.html</a></li><li>Android Debuggerd 简要介绍和源码分析：<a href="https://www.cnblogs.com/lance-ehf/p/4249605.html" target="_blank" rel="noopener">https://www.cnblogs.com/lance-ehf/p/4249605.html</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;任何软件都可能存在BUG，调试和修复BUG伴随着整个开发流程，因此异常分析非常重要。如果是native层发生异常，我们一般称之为叫NE（native exception）&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="android debug" scheme="https://alonealive.github.io/Blog/tags/android-debug/"/>
    
      <category term="android" scheme="https://alonealive.github.io/Blog/tags/android/"/>
    
  </entry>
  
  <entry>
    <title>C++ 对象和类（案例代码）</title>
    <link href="https://alonealive.github.io/Blog/2020/07/15/2020/200715_cpp_ObjAndClass/"/>
    <id>https://alonealive.github.io/Blog/2020/07/15/2020/200715_cpp_ObjAndClass/</id>
    <published>2020-07-15T15:52:00.000Z</published>
    <updated>2020-07-20T14:00:11.522Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>C++类的声明、实现和使用，以及构造函数和析构函数。包含案例代码，可编译运行。</p></blockquote><a id="more"></a><h2 id="类的声明、实现、使用"><a href="#类的声明、实现、使用" class="headerlink" title="类的声明、实现、使用"></a>类的声明、实现、使用</h2><h3 id="类的成员访问控制：公有-私有"><a href="#类的成员访问控制：公有-私有" class="headerlink" title="类的成员访问控制：公有/私有"></a>类的成员访问控制：公有/私有</h3><p>无论类成员是数据成员还是成员函数，都可以在类的公有部分或私有部分中声明他。</p><p>而隐藏数据是OOP（面向对象）主要的目标之一，因此数据项通常放在私有部分，组成类接口的成员函数放在公有部分（否则就无法从程序中调用这些函数）</p><p>也可以把成员函数放在私有部分中，不能直接从程序中调用这种函数，但是公有方法却可以使用他们。通常，程序员使用私有成员函数处理不属于公有接口的实现细节。</p><p>类声明中的关键字private是类对象的默认访问控制，可以不用写出来。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> &#123;</span></span><br><span class="line">    <span class="keyword">float</span> mass; <span class="comment">//private by default</span></span><br><span class="line">    ...</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">call</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Note：结构的默认访问类型是public，而类为private，可以不用再写出来。</p><h3 id="类成员函数实现"><a href="#类成员函数实现" class="headerlink" title="类成员函数实现"></a>类成员函数实现</h3><p>类声明后，需要具体实现原型表示的成员函数。成员函数有函数头和函数体，也可以有返回类型和参数，而其和常规函数的不同之处：</p><ul><li>定义成员函数时，使用作用域解析运算符<code>::</code>来标识函数所属的类。例如<code>void A::update(double price)...</code>，这意味着<code>update()</code>函数是A类的成员。同时意味着可以将另一个类的成员函数也命名成<code>update()</code>，例如<code>void B::update()...</code>。因此作用域解析运算符确定了方法定义对应的类。</li><li>类方法可以访问类的private私有成员，即定义的私有变量这些。</li></ul><h4 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h4><ol><li>头文件：</li></ol><figure class="highlight cpp"><figcaption><span>stock00.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">//stock00.h -- Stock class interfate</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> STOCK00_H_</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> STOCK00_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;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stock</span> &#123;</span></span><br><span class="line"><span class="keyword">private</span>:   <span class="comment">//can remove</span></span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">string</span> company;</span><br><span class="line"><span class="keyword">long</span> shares;</span><br><span class="line"><span class="keyword">double</span> share_val;</span><br><span class="line"><span class="keyword">double</span> total_val;</span><br><span class="line"><span class="comment">//定义于类声明中的函数将自动成为内联函数</span></span><br><span class="line"><span class="comment">//等价于在头文件的类外面使用inline void Stock::set_tot() &#123;...&#125;</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">set_tot</span><span class="params">()</span> </span>&#123; total_val = shares * share_val; &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</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">acquire</span><span class="params">(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">string</span> &amp; co, <span class="keyword">long</span> n, <span class="keyword">double</span> pr)</span></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">buy</span><span class="params">(<span class="keyword">long</span> num, <span class="keyword">double</span> price)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">sell</span><span class="params">(<span class="keyword">long</span> num, <span class="keyword">double</span> price)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">update</span><span class="params">(<span class="keyword">double</span> price)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">show</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</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><ol start="2"><li>函数实现文件：</li></ol><figure class="highlight cpp"><figcaption><span>stock00.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//stock00.cpp -- implementing the Stokc class</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stock00.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Stock::acquire</span><span class="params">(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">string</span> &amp; co, <span class="keyword">long</span> n, <span class="keyword">double</span> pr)</span> </span>&#123;</span><br><span class="line">company = co;</span><br><span class="line"><span class="keyword">if</span> (n &lt; <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Number of shares can't be negative, "</span></span><br><span class="line">  &lt;&lt; company &lt;&lt; <span class="string">" shares set to 0.\n"</span>;</span><br><span class="line">shares = <span class="number">0</span>;</span><br><span class="line">&#125; <span class="keyword">else</span></span><br><span class="line">shares = n;</span><br><span class="line"></span><br><span class="line">share_val = pr;</span><br><span class="line">set_tot();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Stock::buy</span><span class="params">(<span class="keyword">long</span> num, <span class="keyword">double</span> price)</span> </span>&#123;</span><br><span class="line"><span class="keyword">if</span> (num &lt; <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Number of shares purchased can't be negative, "</span></span><br><span class="line">  &lt;&lt; <span class="string">"Transaction is aborted.\n"</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">shares += num;</span><br><span class="line">share_val = price;</span><br><span class="line">set_tot();</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">void</span> <span class="title">Stock::sell</span><span class="params">(<span class="keyword">long</span> num, <span class="keyword">double</span> price)</span> </span>&#123;</span><br><span class="line"><span class="keyword">using</span> <span class="built_in">std</span>::<span class="built_in">cout</span>;</span><br><span class="line"><span class="keyword">if</span> (num &lt; <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Number of shares purchased can't be negative, "</span></span><br><span class="line">                          &lt;&lt; <span class="string">"Transaction is aborted.\n"</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (num &gt; shares) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"You can't sell more than you have, "</span></span><br><span class="line">                          &lt;&lt; <span class="string">"Transaction is aborted.\n"</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">shares -= num;</span><br><span class="line">share_val = price;</span><br><span class="line">set_tot();</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">void</span> <span class="title">Stock::update</span><span class="params">(<span class="keyword">double</span> price)</span> </span>&#123;</span><br><span class="line">share_val = price;</span><br><span class="line">set_tot();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Stock::show</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Company: "</span> &lt;&lt; company</span><br><span class="line">  &lt;&lt; <span class="string">" Share: "</span> &lt;&lt; shares &lt;&lt; <span class="string">'\n'</span></span><br><span class="line">                  &lt;&lt; <span class="string">" Share Price: $"</span> &lt;&lt; share_val</span><br><span class="line">  &lt;&lt; <span class="string">" Total Worth: $"</span> &lt;&lt; total_val &lt;&lt; <span class="string">'\n'</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>类使用</li></ol><figure class="highlight cpp"><figcaption><span>usestock00.cpp </span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="comment">//usestock00.cpp -- the client program</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"stock00.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">Stock f;</span><br><span class="line">f.acquire(<span class="string">"NanoSmart"</span>, <span class="number">20</span>, <span class="number">12.50</span>);</span><br><span class="line">f.show();</span><br><span class="line"></span><br><span class="line">f.buy(<span class="number">15</span>, <span class="number">18.125</span>);</span><br><span class="line">f.show();</span><br><span class="line"></span><br><span class="line">f.sell(<span class="number">400</span>, <span class="number">20.00</span>);</span><br><span class="line">f.show();</span><br><span class="line"></span><br><span class="line">f.buy(<span class="number">300000</span>, <span class="number">40.125</span>);</span><br><span class="line">f.show();</span><br><span class="line"></span><br><span class="line">f.sell(<span class="number">30000</span>, <span class="number">0.125</span>);</span><br><span class="line">f.show();</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></pre></td></tr></table></figure><ol start="4"><li>多个文件编译命令：</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ubuntu@ubuntu-xm:~&#x2F;Documents&#x2F;sunwg&#x2F;cpp$ g++ -c stock00.cpp</span><br><span class="line">ubuntu@ubuntu-xm:~&#x2F;Documents&#x2F;sunwg&#x2F;cpp$ g++ -c usestock00.cpp</span><br><span class="line">ubuntu@ubuntu-xm:~&#x2F;Documents&#x2F;sunwg&#x2F;cpp$ g++ stock00.o usestock00.o -o usestock00</span><br></pre></td></tr></table></figure><ol start="5"><li>执行结果：</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">ubuntu@ubuntu-xm:~&#x2F;Documents&#x2F;sunwg&#x2F;cpp$ .&#x2F;usestock00 </span><br><span class="line">Company: NanoSmart Share: 20</span><br><span class="line"> Share Price: $12.5 Total Worth: $250</span><br><span class="line">Company: NanoSmart Share: 35</span><br><span class="line"> Share Price: $18.125 Total Worth: $634.375</span><br><span class="line">You can&#39;t sell more than you have, Transaction is aborted.</span><br><span class="line">Company: NanoSmart Share: 35</span><br><span class="line"> Share Price: $18.125 Total Worth: $634.375</span><br><span class="line">Company: NanoSmart Share: 300035</span><br><span class="line"> Share Price: $40.125 Total Worth: $1.20389e+07</span><br><span class="line">Company: NanoSmart Share: 270035</span><br><span class="line"> Share Price: $0.125 Total Worth: $33754.4</span><br></pre></td></tr></table></figure><hr><h2 id="类的构造函数和析构函数"><a href="#类的构造函数和析构函数" class="headerlink" title="类的构造函数和析构函数"></a>类的构造函数和析构函数</h2><h3 id="声明构造函数"><a href="#声明构造函数" class="headerlink" title="声明构造函数"></a>声明构造函数</h3><p>构造函数专门用于构造新对象、将值赋给他们的数据成员。</p><p>例如:<code>Stock::Stock(const string &amp; co, long n, double pr) {...}</code></p><p>此处构造函数的参数表示的不是类成员，而是赋给类成员的值。因此，参数名不能与类成员相同，否则会造成混乱。</p><p>常见的做法是在数据成员名中使用<code>m_</code>前缀或者使用后缀<code>_</code>。</p><p>例如：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stock</span> &#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="built_in">string</span> m_company;</span><br><span class="line"><span class="keyword">long</span> m_shares;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>或者</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Stock</span> &#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="built_in">string</span> company_;</span><br><span class="line"><span class="keyword">long</span> shares_;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="使用构造函数"><a href="#使用构造函数" class="headerlink" title="使用构造函数"></a>使用构造函数</h3><p>两种使用构造函数初始化对象的方式：</p><ol><li>显式调用构造函数：<code>Stock food = Stock(&quot;World&quot;, 250, 1.25);</code></li><li>隐式调用构造函数：<code>Stock garment(&quot;Good&quot;, 50, 2.5);</code>，等价于显式的方法：<code>Stock garment = Stock(&quot;Good&quot;, 50, 2.5);</code></li></ol><p>创建类对象的时候，C++都会使用类的构造函数：<code>Stock *pstock = new Stock(&quot;Best&quot;, 18, 1.9);</code></p><p>此处创建了一个Stock对象，并调用构造函数初始化为参数提供的值，将对象的地址赋给pstock指针。此时对象没有名称，但是可以使用指针来管理该对象。</p><p>使用对象调用方法：<code>stock1.show();</code></p><h3 id="析构函数"><a href="#析构函数" class="headerlink" title="析构函数"></a>析构函数</h3><p>如果构造函数使用new来分配内存，则析构函数将使用delete来释放这些内存。</p><p>如果Stock的构造函数没有使用new，则析构函数实际上没有需要完成的任务。在这种情况下，只需让编译器生成一个什么都不做的隐式析构函数即可。</p><p>析构函数可以没有返回值和声明类型。</p><p>和构造函数不同，析构函数没有参数。</p><p>例如Stock析构函数的原型：<code>~Stock();</code></p><p>或者：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Stock::~Sotck &#123;</span><br><span class="line"><span class="built_in">cout</span> &lt;&lt; <span class="string">"Bye"</span> &lt;&lt;<span class="built_in">endl</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;C++类的声明、实现和使用，以及构造函数和析构函数。包含案例代码，可编译运行。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/categories/cpp/"/>
    
    
      <category term="cpp" scheme="https://alonealive.github.io/Blog/tags/cpp/"/>
    
  </entry>
  
  <entry>
    <title>Android HWUI绘制流程</title>
    <link href="https://alonealive.github.io/Blog/2020/07/07/2020/200707_android_HWUI_Draw/"/>
    <id>https://alonealive.github.io/Blog/2020/07/07/2020/200707_android_HWUI_Draw/</id>
    <published>2020-07-07T13:12:00.000Z</published>
    <updated>2020-07-30T12:25:29.326Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Android中绘图的API有很多，比如2D的绘图skia；3D的绘图OpenGLES，Vulkan等。Android在后来完善3D API支持的同时，也在更新View Widget渲染机制，提出了硬件加速机制。  </p></blockquote><a id="more"></a><blockquote><p>HWUI绘制的大致流程是先初始化绘制环境（创建rendernode、渲染线程RenderThread、Context上下文、RenderProxy代理对象），之后是创建DisplayList显示列表，然后开始视图绘制，视图绘制结束后开始同步帧数据。</p></blockquote><blockquote><p><code>硬件加速</code>：<strong>作用</strong>：是将2D的绘图操纵转换为对应的3D绘图操纵。需要显示的时候，再用OpenGLES通过GPU渲染。<strong>过程</strong>：界面创建时，第一次全部渲染，后续界面如果只有部分区域的widget更新，只需要重新渲染更新的widget。渲染好的绘图保存在一个显示列表<code>DisplayList</code>中，需要真正显示到界面的时候，直接显示DisplayList中的绘图。<strong>好处</strong>：一方面利用GPU去渲染，比Skia要快；另一方面，采用DisplayList，再次渲染只更新部分区域，最大程度利用上一帧的数据，提高效率。</p></blockquote><blockquote><p>使用Android Q AOSP源码梳理流程。</p></blockquote><h2 id="GPU渲染（硬件加速）介绍"><a href="#GPU渲染（硬件加速）介绍" class="headerlink" title="GPU渲染（硬件加速）介绍"></a>GPU渲染（硬件加速）介绍</h2><p>在Android应用程序中是通过Canvas API来绘制UI元素的。在硬件加速渲染环境中,这些Canvas API调用最终会转化为OpenGL API调用(转化过程对应用程序来说是透明的)。由于OpenGL API调用要求发生在Open GL环境中,因此在每当有新的Activity窗口启动时,系统都会为其初始化好OpenGL环境。</p><p>这里的渲染,主要是Android硬件加速,即GPU渲染。android上就是通过libhwui调用OpenGL api来渲染, Android P上libhwui 会调用skia,再调用GLES相关的API进行渲染。</p><p>GPU作为一个硬件 , 用户空间是不可以直接使用的, 它是由GPU厂商按照Open GL规范实现的驱动间接进行使用的。也就是说 , 如果一个设备支持GPU硬件加速渲染, 那么当Android应用程序调用OpenGL接口来绘制UI时 ,Android应用程序的UI就是通过硬件加速技术进行渲染的。</p><p><strong>名词介绍：</strong></p><ul><li>GPU:一个类似于CPU的专门用来处理Graphics的处理器, 作用用来帮助加快栅格化操作, 当然, 也有相应的缓存数据(例如缓存已经光栅化过的bitmap等)机制。</li><li>OpenGL ES:是手持嵌入式设备的3DAPI, 跨平台的、功能完善的2D和3D图形应用程序接口API, 有一套固定渲染管线流程</li><li>DisplayList:在Android把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。</li><li>栅格化:是将图片等矢量资源, 转化为一格格像素点的像素图, 显示到屏幕上。</li><li>垂直同步VSYNC:让显卡的运算和显示器刷新率一致以稳定输出的画面质量。它告知GPU在载入新帧之前,要等待屏幕绘制完成前一帧。</li><li>RefreshRate:屏幕一秒内刷新屏幕的次数, 由硬件决定, 例如60Hz</li><li>Frame Rate:GPU一秒绘制操作的帧数, 单位是fps</li></ul><h2 id="Android-5-0-之后的渲染框架"><a href="#Android-5-0-之后的渲染框架" class="headerlink" title="Android 5.0 之后的渲染框架"></a>Android 5.0 之后的渲染框架</h2><p>在Android应用程序窗口中, 每一个View都抽象为一个Render Node, 而且如果一个View设置有Background, 这个background 也被抽象为一个Render Node 。</p><p>这是由于在OpenGLRenderer库中, 并没有View的概念, 所有的一切可绘制的元素都抽象为一个Render Node。</p><p>每一个Render Node都关联有一个<code>DisplayList Renderer</code>, Display List是一个绘制命令缓冲区。当View的成员函数onDraw被调用时, 我们调用通过参数传递进来的Canvas的<code>drawXXX</code>成员函数绘制图形时, 我们实际上只是将对应的绘制命令以及参数保存在一个Display List中。接下来再通过DisplayList Renderer执行这个Display List的命令, 这个过程称为Display List Replay。</p><p>Android应用程序窗口的View是通过树形结构来组织的。这些View不管是通过硬件加速渲染还是软件渲染, 或者是一个特殊的TextureView,在它们的成员函数onDraw被调用期间, 它们都是将自己的UI绘制在ParentView的DisplayList中。</p><p>其中, 最顶层的Parent View是一个Root View, 它关联的RootNode称为<code>Root Render Node</code>。也就是说, 最终Root Render Node的DisplayList将会包含一个窗口的所有绘制命令。</p><p>在绘制窗口的下一帧时, RootRender Node的Display List都会通过一个OpenGL Renderer真正地通过Open GL命令绘制在一个<code>Graphic Buffer</code>中。</p><p>最后这个 Graphic Buffer 被交给 SurfaceFlinger 服务进行合成和显示。</p><h2 id="Android原生硬件绘制案例"><a href="#Android原生硬件绘制案例" class="headerlink" title="Android原生硬件绘制案例"></a>Android原生硬件绘制案例</h2><blockquote><p>这个案例是用的SurfaceView.java的流程。这个流程和实际上从ViewRootImpl.java中通过performDraw的流程类似。可以互相借鉴参考。</p></blockquote><p>Android原生的硬件绘制案例，在<code>frameworks/base/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java</code>：</p><figure class="highlight java"><figcaption><span>HardwareCanvasSurfaceViewActivity.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></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">RenderingThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> SurfaceHolder mSurface;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">boolean</span> mRunning = <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> mWidth, mHeight;</span><br><span class="line">        <span class="comment">//应用拿到一个Surface</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">RenderingThread</span><span class="params">(SurfaceHolder surface)</span> </span>&#123;</span><br><span class="line">            mSurface = surface;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">setSize</span><span class="params">(<span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">            mWidth = width;</span><br><span class="line">            mHeight = height;</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="keyword">float</span> x = <span class="number">0.0f</span>;</span><br><span class="line">            <span class="keyword">float</span> y = <span class="number">0.0f</span>;</span><br><span class="line">            <span class="keyword">float</span> speedX = <span class="number">5.0f</span>;</span><br><span class="line">            <span class="keyword">float</span> speedY = <span class="number">3.0f</span>;</span><br><span class="line"></span><br><span class="line">            Paint paint = <span class="keyword">new</span> Paint();</span><br><span class="line">            paint.setColor(<span class="number">0xff00ff00</span>);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span> (mRunning &amp;&amp; !Thread.interrupted()) &#123;</span><br><span class="line">                <span class="comment">//先调用Surface的lockHardwareCanvas函数</span></span><br><span class="line">                <span class="keyword">final</span> Canvas canvas = mSurface.lockHardwareCanvas();</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">//绘制</span></span><br><span class="line">                    canvas.drawColor(<span class="number">0x00000000</span>, PorterDuff.Mode.CLEAR);</span><br><span class="line">                    canvas.drawRect(x, y, x + <span class="number">20.0f</span>, y + <span class="number">20.0f</span>, paint);</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    <span class="comment">//绘制完成后</span></span><br><span class="line">                    mSurface.unlockCanvasAndPost(canvas);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (x + <span class="number">20.0f</span> + speedX &gt;= mWidth || x + speedX &lt;= <span class="number">0.0f</span>) &#123;</span><br><span class="line">                    speedX = -speedX;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (y + <span class="number">20.0f</span> + speedY &gt;= mHeight || y + speedY &lt;= <span class="number">0.0f</span>) &#123;</span><br><span class="line">                    speedY = -speedY;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                x += speedX;</span><br><span class="line">                y += speedY;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">//每个15s循环一次</span></span><br><span class="line">                    Thread.sleep(<span class="number">15</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    <span class="comment">// Interrupted</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="function"><span class="keyword">void</span> <span class="title">stopRendering</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            interrupt();</span><br><span class="line">            mRunning = <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="Java层相关流程（frameworks-base的View模块和graphics模块）"><a href="#Java层相关流程（frameworks-base的View模块和graphics模块）" class="headerlink" title="Java层相关流程（frameworks/base的View模块和graphics模块）"></a>Java层相关流程（frameworks/base的View模块和graphics模块）</h3><ol><li>首先调用关键函数<code>lockHardwareCanvas</code>，在<code>frameworks/base/core/java/android/view/SurfaceView.java</code>：</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/SurfaceView.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Surface <span class="title">getSurface</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> mSurface;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> Canvas <span class="title">lockHardwareCanvas</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> internalLockCanvas(<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="function"><span class="keyword">private</span> Canvas <span class="title">internalLockCanvas</span><span class="params">(Rect dirty, <span class="keyword">boolean</span> hardware)</span> </span>&#123;</span><br><span class="line">        mSurfaceLock.lock();</span><br><span class="line">        Canvas c = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (!mDrawingStopped &amp;&amp; mSurfaceControl != <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> (hardware) &#123;</span><br><span class="line">                    <span class="comment">//hardware传递的是true，执行lockHardwareCanvas</span></span><br><span class="line">                    c = mSurface.lockHardwareCanvas();</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    c = mSurface.lockCanvas(dirty);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                Log.e(LOG_TAG, <span class="string">"Exception locking surface"</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="keyword">if</span> (c != <span class="keyword">null</span>) &#123;</span><br><span class="line">            mLastLockTime = SystemClock.uptimeMillis();</span><br><span class="line">            <span class="keyword">return</span> c;</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br><span class="line">        mLastLockTime = now;</span><br><span class="line">        mSurfaceLock.unlock();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>然后就调用Surface.java的lockHardwareCanvas函数，此处封装了一个<code>HwuiContext</code>对象，构造函数如下：</p><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/Surface.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">public</span> Canvas <span class="title">lockHardwareCanvas</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      Log.d(TAG, <span class="string">"lockHardwareCanvas"</span>);</span><br><span class="line">      <span class="keyword">synchronized</span> (mLock) &#123;</span><br><span class="line">          checkNotReleasedLocked();</span><br><span class="line">          <span class="keyword">if</span> (mHwuiContext == <span class="keyword">null</span>) &#123;</span><br><span class="line">              <span class="comment">//Step 1 创建HwuiContext，调用构造函数</span></span><br><span class="line">              mHwuiContext = <span class="keyword">new</span> HwuiContext(<span class="keyword">false</span>);</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="comment">//Step 2 调用他的lockCanvas函数</span></span><br><span class="line">          <span class="keyword">return</span> mHwuiContext.lockCanvas(</span><br><span class="line">                  nativeGetWidth(mNativeObject),</span><br><span class="line">                  nativeGetHeight(mNativeObject));</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="function">Canvas <span class="title">lockCanvas</span><span class="params">(<span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">          <span class="keyword">if</span> (mCanvas != <span class="keyword">null</span>) &#123;</span><br><span class="line">              <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Surface was already locked!"</span>);</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="comment">//调用RenderNode的beginRecording函数</span></span><br><span class="line">          mCanvas = mRenderNode.beginRecording(width, height);</span><br><span class="line">          <span class="keyword">return</span> mCanvas;</span><br><span class="line">      &#125;</span><br><span class="line">  .....</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="创建RenderNode"><a href="#创建RenderNode" class="headerlink" title="创建RenderNode"></a>创建RenderNode</h3><blockquote><p>RenderNode用以绘图操纵的批处理，当绘制的时候，可以store和apply。java层的代码如下：其实RenderNode就对应前面我们所说的ViewGroup，有一个RootView，同样也有一个RootNode。</p></blockquote><ol><li>在上面Surface.java调用HwuiContext构造函数的时候，会创建RenderNode对象：</li></ol><figure class="highlight java"><figcaption><span>Surface.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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> <span class="class"><span class="keyword">class</span> <span class="title">HwuiContext</span> </span>&#123;</span><br><span class="line">    <span class="comment">//创建RenderNode和HwuiRender</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RenderNode mRenderNode;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">long</span> mHwuiRenderer;</span><br><span class="line">    <span class="keyword">private</span> RecordingCanvas mCanvas;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">boolean</span> mIsWideColorGamut;</span><br><span class="line">    <span class="comment">//构造函数</span></span><br><span class="line">    HwuiContext(<span class="keyword">boolean</span> isWideColorGamut) &#123;</span><br><span class="line">        <span class="comment">//创建一个RenderNode</span></span><br><span class="line">        mRenderNode = RenderNode.create(<span class="string">"HwuiCanvas"</span>, <span class="keyword">null</span>);</span><br><span class="line">        ......</span><br></pre></td></tr></table></figure><p>创建RenderNode对象：</p><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/RenderNode.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> RenderNode <span class="title">create</span><span class="params">(String name, @Nullable AnimationHost animationHost)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> RenderNode(name, animationHost);</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">RenderNode</span><span class="params">(String name, AnimationHost animationHost)</span> </span>&#123;</span><br><span class="line">    mNativeRenderNode = nCreate(name);</span><br><span class="line">    NoImagePreloadHolder.sRegistry.registerNativeAllocation(<span class="keyword">this</span>, mNativeRenderNode);</span><br><span class="line">    mAnimationHost = animationHost;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>JNI层：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/core/jni/android_view_RenderNode.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> JNINativeMethod gMethods[] = &#123;</span><br><span class="line"><span class="comment">// ----------------------------------------------------------------------------</span></span><br><span class="line"><span class="comment">// Regular JNI</span></span><br><span class="line"><span class="comment">// ----------------------------------------------------------------------------</span></span><br><span class="line">    &#123; <span class="string">"nCreate"</span>,               <span class="string">"(Ljava/lang/String;)J"</span>, (<span class="keyword">void</span>*) android_view_RenderNode_create &#125;,</span><br><span class="line">    &#123; <span class="string">"nGetNativeFinalizer"</span>,   <span class="string">"()J"</span>,    (<span class="keyword">void</span>*) android_view_RenderNode_getNativeFinalizer &#125;,</span><br><span class="line">    &#123; <span class="string">"nOutput"</span>,               <span class="string">"(J)V"</span>,    (<span class="keyword">void</span>*) android_view_RenderNode_output &#125;,</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) &#123;</span><br><span class="line">    <span class="comment">//创建一个native层的rendernode对象</span></span><br><span class="line">    RenderNode* renderNode = <span class="keyword">new</span> RenderNode();</span><br><span class="line">    renderNode-&gt;incStrong(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (name != <span class="literal">NULL</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">char</span>* textArray = env-&gt;GetStringUTFChars(name, <span class="literal">NULL</span>);</span><br><span class="line">        renderNode-&gt;setName(textArray);</span><br><span class="line">        env-&gt;ReleaseStringUTFChars(name, textArray);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">reinterpret_cast</span>&lt;jlong&gt;(renderNode);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>Native层，创建好RenderNode是提供给DisplayListCanvas。</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/RenderNode.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">RenderNode::RenderNode()</span><br><span class="line">        : mUniqueId(generateId())</span><br><span class="line">        , mDirtyPropertyFields(<span class="number">0</span>)</span><br><span class="line">        , mNeedsDisplayListSync(<span class="literal">false</span>)</span><br><span class="line">        , mDisplayList(<span class="literal">nullptr</span>)</span><br><span class="line">        , mStagingDisplayList(<span class="literal">nullptr</span>)</span><br><span class="line">        , mAnimatorManager(*<span class="keyword">this</span>)</span><br><span class="line">        , mParentCount(<span class="number">0</span>) &#123;&#125;</span><br></pre></td></tr></table></figure><h3 id="beginRecording初始化DisplayList"><a href="#beginRecording初始化DisplayList" class="headerlink" title="beginRecording初始化DisplayList"></a>beginRecording初始化DisplayList</h3><p><img src="beginRecording.jpg" alt="初始化DisplayList"></p><ol><li>在Surface.java中通过lockCanvas调用RenderNode对象的<code>beginRecording</code>函数。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/RenderNode.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="meta">@NonNull</span> <span class="function">RecordingCanvas <span class="title">beginRecording</span><span class="params">(<span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">     <span class="keyword">if</span> (mCurrentRecordingCanvas != <span class="keyword">null</span>) &#123;</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(</span><br><span class="line">                 <span class="string">"Recording currently in progress - missing #endRecording() call?"</span>);</span><br><span class="line">     &#125;</span><br><span class="line">     mCurrentRecordingCanvas = RecordingCanvas.obtain(<span class="keyword">this</span>, width, height);</span><br><span class="line">     <span class="keyword">return</span> mCurrentRecordingCanvas;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>接着调用RecordingCanvas的obtain函数：</p><p><strong>类的继承关系：</strong> RecordingCanvas类继承DisplayListCanvas，而DisplayListCanvas继承BaseRecordingCanvas，<br>BaseRecordingCanvas继承Canvas（继承BaseCanvas）。</p><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/RecordingCanvas.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">RecordingCanvas</span> <span class="keyword">extends</span> <span class="title">DisplayListCanvas</span> </span>&#123;</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="function"><span class="keyword">protected</span> <span class="title">RecordingCanvas</span><span class="params">(@NonNull RenderNode node, <span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(nCreateDisplayListCanvas(node.mNativeRenderNode, width, height));</span><br><span class="line">        mDensity = <span class="number">0</span>; <span class="comment">// disable bitmap density scaling</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> RecordingCanvas <span class="title">obtain</span><span class="params">(@NonNull RenderNode node, <span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (node == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"node cannot be null"</span>);</span><br><span class="line">        RecordingCanvas canvas = sPool.acquire();</span><br><span class="line">        <span class="keyword">if</span> (canvas == <span class="keyword">null</span>) &#123;</span><br><span class="line">            canvas = <span class="keyword">new</span> RecordingCanvas(node, width, height);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//创建一个native的DisplayListCanvas对象（即显示列表的Canvas）</span></span><br><span class="line">            <span class="comment">//JNI通过mNativeCanvasWrapper（BaseCanvas.java创建）找对应的Native的Canvas</span></span><br><span class="line">            nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode,</span><br><span class="line">                    width, height);</span><br><span class="line">        &#125;</span><br><span class="line">        canvas.mNode = node;</span><br><span class="line">        canvas.mWidth = width;</span><br><span class="line">        canvas.mHeight = height;</span><br><span class="line">        <span class="keyword">return</span> canvas;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>查看测试案例代码中的<code>canvas.drawColor</code>和<code>canvas.drawRect</code>函数，是调用了其父类<code>BaseCanvas</code>的对应方法。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/Canvas.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">drawColor</span><span class="params">(@ColorLong <span class="keyword">long</span> color)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>.drawColor(color, BlendMode.SRC_OVER);</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">drawRect</span><span class="params">(@NonNull RectF rect, @NonNull Paint paint)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>.drawRect(rect, paint);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>父类<code>BaseCanvas.java</code>：</p><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/BaseCanvas.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">drawColor</span><span class="params">(@ColorInt <span class="keyword">int</span> color, @NonNull PorterDuff.Mode mode)</span> </span>&#123;</span><br><span class="line">      nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);</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">drawRect</span><span class="params">(@NonNull Rect r, @NonNull Paint paint)</span> </span>&#123;</span><br><span class="line">      throwIfHasHwBitmapInSwMode(paint);</span><br><span class="line">      drawRect(r.left, r.top, r.right, r.bottom, paint);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>Native层Canvas创建（JNI和HWUI模块）</li></ol><p>1.在上面RecordingCanvas.java的构造函数中调用了<code>nCreateDisplayListCanvas</code>函数，对饮的JNI实现：</p><figure class="highlight cpp"><figcaption><span>frameworks/base/core/jni/android_view_DisplayListCanvas.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span>* <span class="keyword">const</span> kClassPathName = <span class="string">"android/graphics/RecordingCanvas"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> JNINativeMethod gMethods[] = &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ------------ @FastNative ------------------</span></span><br><span class="line"></span><br><span class="line">    &#123; <span class="string">"nCallDrawGLFunction"</span>, <span class="string">"(JJLjava/lang/Runnable;)V"</span>,</span><br><span class="line">            (<span class="keyword">void</span>*) android_view_DisplayListCanvas_callDrawGLFunction &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ------------ @CriticalNative --------------</span></span><br><span class="line">    &#123; <span class="string">"nCreateDisplayListCanvas"</span>, <span class="string">"(JII)J"</span>,     (<span class="keyword">void</span>*) android_view_DisplayListCanvas_createDisplayListCanvas &#125;,</span><br><span class="line">    &#123; <span class="string">"nResetDisplayListCanvas"</span>,  <span class="string">"(JJII)V"</span>,    (<span class="keyword">void</span>*) android_view_DisplayListCanvas_resetDisplayListCanvas &#125;,</span><br><span class="line">    &#123; <span class="string">"nGetMaximumTextureWidth"</span>,  <span class="string">"()I"</span>,        (<span class="keyword">void</span>*) android_view_DisplayListCanvas_getMaxTextureSize &#125;,</span><br><span class="line">    &#123; <span class="string">"nGetMaximumTextureHeight"</span>, <span class="string">"()I"</span>,        (<span class="keyword">void</span>*) android_view_DisplayListCanvas_getMaxTextureSize &#125;,</span><br><span class="line">    &#123; <span class="string">"nInsertReorderBarrier"</span>,    <span class="string">"(JZ)V"</span>,      (<span class="keyword">void</span>*) android_view_DisplayListCanvas_insertReorderBarrier &#125;,</span><br><span class="line">    &#123; <span class="string">"nFinishRecording"</span>,         <span class="string">"(J)J"</span>,       (<span class="keyword">void</span>*) android_view_DisplayListCanvas_finishRecording &#125;,</span><br><span class="line">    &#123; <span class="string">"nDrawRenderNode"</span>,          <span class="string">"(JJ)V"</span>,      (<span class="keyword">void</span>*) android_view_DisplayListCanvas_drawRenderNode &#125;,</span><br><span class="line">    &#123; <span class="string">"nDrawTextureLayer"</span>,        <span class="string">"(JJ)V"</span>,      (<span class="keyword">void</span>*) android_view_DisplayListCanvas_drawTextureLayer &#125;,</span><br><span class="line">    &#123; <span class="string">"nDrawCircle"</span>,              <span class="string">"(JJJJJ)V"</span>,   (<span class="keyword">void</span>*) android_view_DisplayListCanvas_drawCircleProps &#125;,</span><br><span class="line">    &#123; <span class="string">"nDrawRoundRect"</span>,           <span class="string">"(JJJJJJJJ)V"</span>,(<span class="keyword">void</span>*) android_view_DisplayListCanvas_drawRoundRectProps &#125;,</span><br><span class="line">    &#123; <span class="string">"nDrawWebViewFunctor"</span>,      <span class="string">"(JI)V"</span>,      (<span class="keyword">void</span>*) android_view_DisplayListCanvas_drawWebViewFunctor &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//具体实现，此处的renderNodePtr变量是RenderNode在native层的对象（地址）</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> jlong <span class="title">android_view_DisplayListCanvas_createDisplayListCanvas</span><span class="params">(jlong renderNodePtr,</span></span></span><br><span class="line"><span class="function"><span class="params">        jint width, jint height)</span> </span>&#123;</span><br><span class="line">    RenderNode* renderNode = <span class="keyword">reinterpret_cast</span>&lt;RenderNode*&gt;(renderNodePtr);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">reinterpret_cast</span>&lt;jlong&gt;(Canvas::create_recording_canvas(width, height, renderNode));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用到frameworks/base/libs/hwui/hwui/Canvas.cpp，<strong>在Andorid Q中，此处只调用了<code>SkiaRecordingCanvas</code>函数。使用skia进行绘制。</strong></p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/hwui/Canvas.cpp</span></figcaption><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">Canvas* <span class="title">Canvas::create_recording_canvas</span><span class="params">(<span class="keyword">int</span> width, <span class="keyword">int</span> height, uirenderer::RenderNode* renderNode)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后初始化DisplayList：</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h</span></figcaption><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">explicit</span> <span class="title">SkiaRecordingCanvas</span><span class="params">(uirenderer::RenderNode* renderNode, <span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">    initDisplayList(renderNode, width, height);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">void</span> <span class="title">SkiaRecordingCanvas::initDisplayList</span><span class="params">(uirenderer::RenderNode* renderNode, <span class="keyword">int</span> width,</span></span></span><br><span class="line"><span class="function"><span class="params">                                          <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">    mCurrentBarrier = <span class="literal">nullptr</span>;</span><br><span class="line">    SkASSERT(mDisplayList.get() == <span class="literal">nullptr</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (renderNode) &#123;</span><br><span class="line">        mDisplayList = renderNode-&gt;detachAvailableList();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!mDisplayList) &#123;</span><br><span class="line">        mDisplayList.reset(<span class="keyword">new</span> SkiaDisplayList());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    mDisplayList-&gt;attachRecorder(&amp;mRecorder, SkIRect::MakeWH(width, height));</span><br><span class="line">    SkiaCanvas::reset(&amp;mRecorder);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Draw绘制操纵"><a href="#Draw绘制操纵" class="headerlink" title="Draw绘制操纵"></a>Draw绘制操纵</h3><blockquote><p>正常流程的绘制是在frameworks/base/core/java/android/view/ThreadedRenderer.java的<code>updateRootDisplayList</code>函数中。<br>调用到drawRnderNode函数绘制。</p></blockquote><ol><li>Java层：上面案例中的<code>drawColor</code>和<code>drawRect</code>实际调用的是在frameworks/base/graphics/java/android/graphics/BaseCanvas.java中：</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/BaseCanvas.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">drawColor</span><span class="params">(@ColorInt <span class="keyword">int</span> color, @NonNull PorterDuff.Mode mode)</span> </span>&#123;</span><br><span class="line">      nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);</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">drawRect</span><span class="params">(@NonNull Rect r, @NonNull Paint paint)</span> </span>&#123;</span><br><span class="line">      throwIfHasHwBitmapInSwMode(paint);</span><br><span class="line">      drawRect(r.left, r.top, r.right, r.bottom, paint);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>调用JNI层在：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/core/jni/android_graphics_Canvas.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">const</span> JNINativeMethod gDrawMethods[] = &#123;</span><br><span class="line">    &#123;<span class="string">"nDrawColor"</span>,<span class="string">"(JII)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawColor&#125;,</span><br><span class="line">    &#123;<span class="string">"nDrawColor"</span>,<span class="string">"(JJJI)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawColorLong&#125;,</span><br><span class="line">    &#123;<span class="string">"nDrawPaint"</span>,<span class="string">"(JJ)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawPaint&#125;,</span><br><span class="line">    &#123;<span class="string">"nDrawPoint"</span>, <span class="string">"(JFFJ)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawPoint&#125;,</span><br><span class="line">    &#123;<span class="string">"nDrawPoints"</span>, <span class="string">"(J[FIIJ)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawPoints&#125;,</span><br><span class="line">    &#123;<span class="string">"nDrawLine"</span>, <span class="string">"(JFFFFJ)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawLine&#125;,</span><br><span class="line">    &#123;<span class="string">"nDrawLines"</span>, <span class="string">"(J[FIIJ)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawLines&#125;,</span><br><span class="line">    &#123;<span class="string">"nDrawRect"</span>,<span class="string">"(JFFFFJ)V"</span>, (<span class="keyword">void</span>*) CanvasJNI::drawRect&#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">static</span> <span class="keyword">void</span> drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) &#123;</span><br><span class="line">    SkBlendMode mode = <span class="keyword">static_cast</span>&lt;SkBlendMode&gt;(modeHandle);</span><br><span class="line">    get_canvas(canvasHandle)-&gt;drawColor(color, mode);</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">drawRect</span><span class="params">(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,</span></span></span><br><span class="line"><span class="function"><span class="params">                     jfloat right, jfloat bottom, jlong paintHandle)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> Paint* paint = <span class="keyword">reinterpret_cast</span>&lt;Paint*&gt;(paintHandle);</span><br><span class="line">    get_canvas(canvasHandle)-&gt;drawRect(left, top, right, bottom, *paint);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>Native层：因为<code>class SkiaCanvas : public Canvas</code>，所以调用到SkiaCanvas类中的具体实现：</li></ol><p>调用</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/SkiaCanvas.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">void</span> <span class="title">SkiaCanvas::drawColor</span><span class="params">(<span class="keyword">int</span> color, SkBlendMode mode)</span> </span>&#123;</span><br><span class="line">    mCanvas-&gt;drawColor(color, mode);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SkiaCanvas::drawRect</span><span class="params">(<span class="keyword">float</span> left, <span class="keyword">float</span> top, <span class="keyword">float</span> right, <span class="keyword">float</span> bottom, <span class="keyword">const</span> SkPaint&amp; paint)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(paint.nothingToDraw())) <span class="keyword">return</span>;</span><br><span class="line">    mCanvas-&gt;drawRect(&#123;left, top, right, bottom&#125;, *filterPaint(paint));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>调用到<code>external/skia</code>图形库模块：</li></ol><blockquote><p>Skia是Google一个底层的图形、图像、动画、SVG、文本等多方面的图形库，是Android中图形系统的引擎。Skia作为第三方软件放在目录：<code>external/skia/</code>。</p></blockquote><p>主要包含三个库：</p><ul><li>libcorecg.so: 包含<code>/skia/src/core</code>的部分内容，比如其中的<code>Region、Rect</code>是在SurfaceFlinger里面用来计算可视区域的；</li><li>libsgl.so: 包含<code>/skia/src/core|effects|images|ports|utils</code>的部分和全部内容，这个实现了skia大部分的图形效果，以及图形格式的编解码；</li><li>libskiagl.so: 包含<code>/skia/src/gl</code>里面的内容，主要用来调用opengl实现部分效果。</li></ul><figure class="highlight cpp"><figcaption><span>external/skia/src/core/SkCanvas.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">void</span> <span class="title">SkCanvas::drawColor</span><span class="params">(SkColor c, SkBlendMode mode)</span> </span>&#123;</span><br><span class="line">    SkPaint paint;</span><br><span class="line">    paint.setColor(c);</span><br><span class="line">    paint.setBlendMode(mode);</span><br><span class="line">    <span class="keyword">this</span>-&gt;drawPaint(paint);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">SkCanvas::drawRect</span><span class="params">(<span class="keyword">const</span> SkRect&amp; r, <span class="keyword">const</span> SkPaint&amp; paint)</span> </span>&#123;</span><br><span class="line">    TRACE_EVENT0(<span class="string">"skia"</span>, TRACE_FUNC);</span><br><span class="line">    <span class="comment">// To avoid redundant logic in our culling code and various backends, we always sort rects</span></span><br><span class="line">    <span class="comment">// before passing them along.</span></span><br><span class="line">    <span class="keyword">this</span>-&gt;onDrawRect(r.makeSorted(), paint);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="HwuiContext和HwuiRenderer"><a href="#HwuiContext和HwuiRenderer" class="headerlink" title="HwuiContext和HwuiRenderer"></a>HwuiContext和HwuiRenderer</h3><h4 id="Java层"><a href="#Java层" class="headerlink" title="Java层"></a>Java层</h4><p>从上面的Surface.java中看到，nHwuiCreate创建HwuiRenderer。</p><figure class="highlight java"><figcaption><span>Surface.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建一个native的HwuiRender对象</span></span><br><span class="line">mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject,</span><br><span class="line">        isWideColorGamut);</span><br><span class="line">        ......</span><br></pre></td></tr></table></figure><h4 id="JNI层"><a href="#JNI层" class="headerlink" title="JNI层"></a>JNI层</h4><figure class="highlight cpp"><figcaption><span>frameworks/base/core/jni/android_view_Surface.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">static</span> <span class="keyword">const</span> JNINativeMethod gSurfaceMethods[] = &#123;</span><br><span class="line">    ......</span><br><span class="line">    <span class="comment">// HWUI context</span></span><br><span class="line">    &#123;<span class="string">"nHwuiCreate"</span>, <span class="string">"(JJZ)J"</span>, (<span class="keyword">void</span>*) hwui::create &#125;,</span><br><span class="line">    &#123;<span class="string">"nHwuiSetSurface"</span>, <span class="string">"(JJ)V"</span>, (<span class="keyword">void</span>*) hwui::setSurface &#125;,</span><br><span class="line">    &#123;<span class="string">"nHwuiDraw"</span>, <span class="string">"(J)V"</span>, (<span class="keyword">void</span>*) hwui::draw &#125;,</span><br><span class="line">    &#123;<span class="string">"nHwuiDestroy"</span>, <span class="string">"(J)V"</span>, (<span class="keyword">void</span>*) hwui::destroy &#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="keyword">namespace</span> uirenderer &#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> android::uirenderer::renderthread;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ContextFactory</span> :</span> <span class="keyword">public</span> IContextFactory &#123;</span><br><span class="line">......</span><br><span class="line"><span class="function"><span class="keyword">static</span> jlong <span class="title">create</span><span class="params">(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfacePtr,</span></span></span><br><span class="line"><span class="function"><span class="params">        jboolean isWideColorGamut)</span> </span>&#123;</span><br><span class="line">    RenderNode* rootNode = <span class="keyword">reinterpret_cast</span>&lt;RenderNode*&gt;(rootNodePtr);</span><br><span class="line">    <span class="function">sp&lt;Surface&gt; <span class="title">surface</span><span class="params">(<span class="keyword">reinterpret_cast</span>&lt;Surface*&gt;(surfacePtr))</span></span>;</span><br><span class="line">    ContextFactory factory;</span><br><span class="line">    <span class="comment">//创建一个RenderProxy对象，并作为返回对象</span></span><br><span class="line">    RenderProxy* proxy = <span class="keyword">new</span> RenderProxy(<span class="literal">false</span>, rootNode, &amp;factory);</span><br><span class="line">    proxy-&gt;loadSystemProperties();</span><br><span class="line">    <span class="keyword">if</span> (isWideColorGamut) &#123;</span><br><span class="line">        proxy-&gt;setWideGamut(<span class="literal">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    proxy-&gt;setSwapBehavior(SwapBehavior::kSwap_discardBuffer);</span><br><span class="line">    proxy-&gt;setSurface(surface);</span><br><span class="line">    <span class="comment">// Shadows can't be used via this interface, so just set the light source</span></span><br><span class="line">    <span class="comment">// to all 0s.</span></span><br><span class="line">    proxy-&gt;setLightAlpha(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    proxy-&gt;setLightGeometry((Vector3)&#123;<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>&#125;, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">return</span> (jlong) proxy;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><blockquote><p><strong>以下流程部分是和通常的HWUI环境初始化流程相同的。</strong></p></blockquote><h4 id="native层：调用RenderProxy-cpp构造函数"><a href="#native层：调用RenderProxy-cpp构造函数" class="headerlink" title="*native层：调用RenderProxy.cpp构造函数"></a>*native层：调用RenderProxy.cpp构造函数</h4><p>该构造函数的几个重要成员变量：</p><ul><li>RenderProxy是一个代理者，严格的单线程。所有的方法都必须在自己的线程中调用。<code>MainThread通过这个代理对象想Task Queue发送drawFrame命令</code>；</li><li>RenderThread（即构造函数中的mRenderThread）：渲染线程，是一个单例，也就是说，一个进程中只有一个，所有的绘制操纵都必须在这个线程中完成。应用端很多操纵，都以RenderTask的形式post到RenderThread线程中完成。（在Android 5.0之后独立出来的应用程序的OpenGL线程）</li><li>CanvasContext（即构造函数中的mContext）：上下文，由于OpenGL是单线程的，所以，我们给到GPU的绘图命令都封装在各自的上下文中。这个和上层的HwuiRenderer是对应的。（将窗口绑定到Open GL渲染上下文中，从而使后面的渲染操作都是针对被绑定窗口的）</li><li>DrawFrameTask（即构造函数中的mDrawFrameTask）：一个用来执行渲染任务的task，MainThread通过他向RenderThread线程发送渲染下一帧的命令。（比较特殊的一个RenderTask，可重复使用的绘制Task。）</li></ul><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/RenderProxy.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> android &#123;</span><br><span class="line"><span class="keyword">namespace</span> uirenderer &#123;</span><br><span class="line"><span class="keyword">namespace</span> renderthread &#123;</span><br><span class="line"></span><br><span class="line">RenderProxy::RenderProxy(<span class="keyword">bool</span> translucent, RenderNode* rootRenderNode,</span><br><span class="line">                         IContextFactory* contextFactory)</span><br><span class="line">        : mRenderThread(RenderThread::getInstance()), mContext(<span class="literal">nullptr</span>) &#123;  <span class="comment">//Step1 创建RenderThread渲染线程</span></span><br><span class="line">    mContext = mRenderThread.<span class="built_in">queue</span>().runSync([&amp;]() -&gt; CanvasContext* &#123;</span><br><span class="line">        <span class="keyword">return</span> CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory); <span class="comment">//Step2 创建CanvasContext渲染上下文</span></span><br><span class="line">    &#125;);</span><br><span class="line">    mDrawFrameTask.setContext(&amp;mRenderThread, mContext, rootRenderNode);</span><br><span class="line">&#125;</span><br><span class="line">......</span><br></pre></td></tr></table></figure><hr><h3 id="创建RenderThread渲染线程"><a href="#创建RenderThread渲染线程" class="headerlink" title="创建RenderThread渲染线程"></a>创建RenderThread渲染线程</h3><ol><li>从上面构造函数中的<code>RenderThread::getInstance()</code>调用下去。创建一个RenderThread线程。</li></ol><p>而该类的父类是ThreadBase.h，父类的父类是Thread.h</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/RenderThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">RenderThread&amp; <span class="title">RenderThread::getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// This is a pointer because otherwise __cxa_finalize</span></span><br><span class="line">    <span class="comment">// will try to delete it like a Good Citizen but that causes us to crash</span></span><br><span class="line">    <span class="comment">// because we don't want to delete the RenderThread normally.</span></span><br><span class="line">    <span class="keyword">static</span> RenderThread* sInstance = <span class="keyword">new</span> RenderThread();</span><br><span class="line">    gHasRenderThreadInstance = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">return</span> *sInstance;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">RenderThread::RenderThread()</span><br><span class="line">        : ThreadBase()</span><br><span class="line">        , mVsyncSource(<span class="literal">nullptr</span>)</span><br><span class="line">        , mVsyncRequested(<span class="literal">false</span>)</span><br><span class="line">        , mFrameCallbackTaskPending(<span class="literal">false</span>)</span><br><span class="line">        , mRenderState(<span class="literal">nullptr</span>)</span><br><span class="line">        , mEglManager(<span class="literal">nullptr</span>)</span><br><span class="line">        , mFunctorManager(WebViewFunctorManager::instance())</span><br><span class="line">        , mVkManager(<span class="literal">nullptr</span>) &#123;</span><br><span class="line">    Properties::load();</span><br><span class="line">    <span class="comment">//实现父类的函数，调用run</span></span><br><span class="line">    start(<span class="string">"RenderThread"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>父类创建looper循环和start函数实现，然后开始线程循环：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/thread/ThreadBase.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">ThreadBase()</span><br><span class="line">        : Thread(<span class="literal">false</span>)</span><br><span class="line">        , mLooper(<span class="keyword">new</span> Looper(<span class="literal">false</span>))</span><br><span class="line">        , mQueue([<span class="keyword">this</span>]() &#123; mLooper-&gt;wake(); &#125;, mLock) &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">start</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* name = <span class="string">"ThreadBase"</span>)</span> </span>&#123; Thread::run(name); &#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>开始线程循环，调用ThreadLoop开始工作。</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/RenderThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">RenderThread::threadLoop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    initThreadLocals();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        waitForWork();</span><br><span class="line">        processQueue();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mPendingRegistrationFrameCallbacks.size() &amp;&amp; !mFrameCallbackTaskPending) &#123;</span><br><span class="line">            drainDisplayEventQueue();</span><br><span class="line">            mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),</span><br><span class="line">                                   mPendingRegistrationFrameCallbacks.end());</span><br><span class="line">            mPendingRegistrationFrameCallbacks.clear();</span><br><span class="line">            requestVsync();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!mFrameCallbackTaskPending &amp;&amp; !mVsyncRequested &amp;&amp; mFrameCallbacks.size()) &#123;</span><br><span class="line">            requestVsync();</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="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//以下流程会处理vsync信号</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">RenderThread::initThreadLocals</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    setupFrameInterval();</span><br><span class="line">    initializeDisplayEventReceiver();</span><br><span class="line">    mEglManager = <span class="keyword">new</span> EglManager();</span><br><span class="line">    mRenderState = <span class="keyword">new</span> RenderState(*<span class="keyword">this</span>);</span><br><span class="line">    mVkManager = <span class="keyword">new</span> VulkanManager();</span><br><span class="line">    mCacheManager = <span class="keyword">new</span> CacheManager(DeviceInfo::get()-&gt;displayInfo());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">RenderThread::initializeDisplayEventReceiver</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!Properties::isolatedProcess) &#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">// Register the FD</span></span><br><span class="line">        mLooper-&gt;addFd(receiver-&gt;getFd(), <span class="number">0</span>, Looper::EVENT_INPUT,</span><br><span class="line">                       RenderThread::displayEventReceiverCallback, <span class="keyword">this</span>);</span><br><span class="line">        mVsyncSource = <span class="keyword">new</span> DisplayEventReceiverWrapper(<span class="built_in">std</span>::move(receiver), [<span class="keyword">this</span>] &#123;</span><br><span class="line">            DeviceInfo::get()-&gt;onDisplayConfigChanged();</span><br><span class="line">            setupFrameInterval();</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        mVsyncSource = <span class="keyword">new</span> DummyVsyncSource(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">RenderThread::displayEventReceiverCallback</span><span class="params">(<span class="keyword">int</span> fd, <span class="keyword">int</span> events, <span class="keyword">void</span>* data)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">reinterpret_cast</span>&lt;RenderThread*&gt;(data)-&gt;drainDisplayEventQueue();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;  <span class="comment">// keep the callback</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">RenderThread::drainDisplayEventQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    <span class="keyword">nsecs_t</span> vsyncEvent = mVsyncSource-&gt;latestVsyncEvent();</span><br><span class="line">    <span class="keyword">if</span> (vsyncEvent &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        mVsyncRequested = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (mTimeLord.vsyncReceived(vsyncEvent) &amp;&amp; !mFrameCallbackTaskPending) &#123;</span><br><span class="line">            ATRACE_NAME(<span class="string">"queue mFrameCallbackTask"</span>);</span><br><span class="line">            mFrameCallbackTaskPending = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">nsecs_t</span> runAt = (vsyncEvent + mDispatchFrameDelay);</span><br><span class="line">            <span class="built_in">queue</span>().postAt(runAt, [<span class="keyword">this</span>]() &#123; dispatchFrameCallbacks(); &#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><hr><h3 id="CanvasContext渲染上下文"><a href="#CanvasContext渲染上下文" class="headerlink" title="CanvasContext渲染上下文"></a>CanvasContext渲染上下文</h3><p>该函数中会选择渲染绘制方式（渲染管线）。在Android Q中取消了OpenGL渲染。</p><ul><li>Android P之前：<code>enum class RenderPipelineType { OpenGL = 0, SkiaGL, SkiaVulkan, NotInitialized = 128 };</code></li><li>Android Q：<code>enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };</code></li></ul><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/CanvasContext.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">CanvasContext* <span class="title">CanvasContext::create</span><span class="params">(RenderThread&amp; thread, <span class="keyword">bool</span> translucent,</span></span></span><br><span class="line"><span class="function"><span class="params">                                     RenderNode* rootRenderNode, IContextFactory* contextFactory)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//获取渲染方式getRenderPipelineType</span></span><br><span class="line">    <span class="keyword">auto</span> renderType = Properties::getRenderPipelineType();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">switch</span> (renderType) &#123;</span><br><span class="line">        <span class="keyword">case</span> RenderPipelineType::SkiaGL:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> CanvasContext(thread, translucent, rootRenderNode, contextFactory,</span><br><span class="line">                                     <span class="built_in">std</span>::make_unique&lt;skiapipeline::SkiaOpenGLPipeline&gt;(thread));</span><br><span class="line">        <span class="keyword">case</span> RenderPipelineType::SkiaVulkan:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> CanvasContext(thread, translucent, rootRenderNode, contextFactory,</span><br><span class="line">                                     <span class="built_in">std</span>::make_unique&lt;skiapipeline::SkiaVulkanPipeline&gt;(thread));</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            LOG_ALWAYS_FATAL(<span class="string">"canvas context type %d not supported"</span>, (<span class="keyword">int32_t</span>)renderType);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">RenderPipelineType <span class="title">Properties::getRenderPipelineType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sRenderPipelineType = peekRenderPipelineType();</span><br><span class="line">    <span class="keyword">return</span> sRenderPipelineType;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">RenderPipelineType <span class="title">Properties::peekRenderPipelineType</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// If sRenderPipelineType has been locked, just return the locked type immediately.</span></span><br><span class="line">    <span class="keyword">if</span> (sRenderPipelineType != RenderPipelineType::NotInitialized) &#123;</span><br><span class="line">        <span class="keyword">return</span> sRenderPipelineType;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">bool</span> useVulkan = use_vulkan().value_or(<span class="literal">false</span>);</span><br><span class="line">    <span class="keyword">char</span> prop[PROPERTY_VALUE_MAX];</span><br><span class="line">    <span class="comment">//PROPERTY_RENDERER "debug.hwui.renderer"</span></span><br><span class="line">    <span class="comment">//enum class RenderPipelineType &#123; SkiaGL, SkiaVulkan, NotInitialized = 128 &#125;;</span></span><br><span class="line">    property_get(PROPERTY_RENDERER, prop, useVulkan ? <span class="string">"skiavk"</span> : <span class="string">"skiagl"</span>);</span><br><span class="line">    <span class="keyword">if</span> (!<span class="built_in">strcmp</span>(prop, <span class="string">"skiavk"</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span> RenderPipelineType::SkiaVulkan;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> RenderPipelineType::SkiaGL;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>mRenderPipeline有几种类型，创建CanvasContext时（<code>create函数</code>），会根据pipeline的类型，创建对应的Pipeline。（即调用<code>getRenderPipelineType函数</code>）</p></blockquote><ul><li>渲染类型：<code>enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };</code></li></ul><hr><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/CanvasContext.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">CanvasContext::CanvasContext(RenderThread&amp; thread, <span class="keyword">bool</span> translucent, RenderNode* rootRenderNode,</span><br><span class="line">                             IContextFactory* contextFactory,</span><br><span class="line">                             <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;IRenderPipeline&gt; renderPipeline)</span><br><span class="line">        : mRenderThread(thread)</span><br><span class="line">        , mGenerationID(<span class="number">0</span>)</span><br><span class="line">        , mOpaque(!translucent)</span><br><span class="line">        , mAnimationContext(contextFactory-&gt;createAnimationContext(mRenderThread.timeLord()))</span><br><span class="line">        , mJankTracker(&amp;thread.globalProfileData(), DeviceInfo::get()-&gt;displayInfo())</span><br><span class="line">        , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())</span><br><span class="line">        , mContentDrawBounds(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>)</span><br><span class="line">        , mRenderPipeline(<span class="built_in">std</span>::move(renderPipeline)) &#123;</span><br><span class="line">    rootRenderNode-&gt;makeRoot();</span><br><span class="line">    mRenderNodes.emplace_back(rootRenderNode);  <span class="comment">//是前面创建的RenderNode</span></span><br><span class="line">    mProfiler.setDensity(DeviceInfo::get()-&gt;displayInfo().density);</span><br><span class="line">    setRenderAheadDepth(Properties::defaultRenderAhead);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在上面调用nHwuiCreate的JNI层，会创建ContextFactory，然后在此处的构造函数中会使用到。主要用来创建AnimationContext，即<code>mAnimationContext(contextFactory-&gt;createAnimationContext(mRenderThread.timeLord()))</code></p><p>AnimationContext主要用来处理动画Animation。</p><figure class="highlight cpp"><figcaption><span>frameworks/base/core/jni/android_view_Surface.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="class"><span class="keyword">class</span> <span class="title">ContextFactory</span> :</span> <span class="keyword">public</span> IContextFactory &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> AnimationContext* <span class="title">createAnimationContext</span><span class="params">(renderthread::TimeLord&amp; clock)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> AnimationContext(clock);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><ul><li>CanvasContext实现了IFrameCallback接口，所以，CanvasContext能接收编舞者Choreographer的callback，处理实时动画。<code>class CanvasContext : public IFrameCallback {...}</code></li></ul><h4 id="RenderThread渲染线程"><a href="#RenderThread渲染线程" class="headerlink" title="RenderThread渲染线程"></a>RenderThread渲染线程</h4><blockquote><p><strong>RenderThread渲染运行模型</strong>:空闲的时候, RenderThread睡眠在成员变量mLooper指向的一个Looper对象的成员函数pollOnceh中。当其他线程需要调度RenderThread, 会向他的任务队列添加一个任务, 然后唤醒RenderThread进行处理。RenderThread通过<code>processQueue</code>方法处理任务。</p></blockquote><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/RenderThread.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//继承ThreadBase，而ThreadBase是继承基类Thread</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RenderThread</span> :</span> <span class="keyword">private</span> ThreadBase &#123;</span><br><span class="line">    <span class="comment">//组织拷贝构造函数和重载</span></span><br><span class="line">    PREVENT_COPY_AND_ASSIGN(RenderThread);</span><br></pre></td></tr></table></figure><ol><li>调用构造函数中，同时启动了渲染线程RenderThread：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/RenderThread.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">RenderThread::RenderThread()</span><br><span class="line">        : ThreadBase()</span><br><span class="line">        , mVsyncSource(<span class="literal">nullptr</span>)</span><br><span class="line">        , mVsyncRequested(<span class="literal">false</span>)</span><br><span class="line">        , mFrameCallbackTaskPending(<span class="literal">false</span>)</span><br><span class="line">        , mRenderState(<span class="literal">nullptr</span>)</span><br><span class="line">        , mEglManager(<span class="literal">nullptr</span>)</span><br><span class="line">        , mFunctorManager(WebViewFunctorManager::instance())</span><br><span class="line">        , mVkManager(<span class="literal">nullptr</span>) &#123;</span><br><span class="line">    Properties::load();</span><br><span class="line">    start(<span class="string">"RenderThread"</span>); <span class="comment">//线程启动</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>查看父类的构造函数：</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/thread/ThreadBase.h</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">ThreadBase()</span><br><span class="line">        : Thread(<span class="literal">false</span>)</span><br><span class="line">        , mLooper(<span class="keyword">new</span> Looper(<span class="literal">false</span>))</span><br><span class="line">        , mQueue([<span class="keyword">this</span>]() &#123;</span><br><span class="line">             mLooper-&gt;wake();  <span class="comment">//此处调用是唤醒mLooper，线程开始工作</span></span><br><span class="line">        &#125;, mLock) &#123;&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>在渲染线程启动后，会调用RenderThread.cpp的threadLoop函数。</li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">RenderThread::threadLoop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    setpriority(PRIO_PROCESS, <span class="number">0</span>, PRIORITY_DISPLAY);</span><br><span class="line">    Looper::setForThread(mLooper);</span><br><span class="line">    <span class="keyword">if</span> (gOnStartHook) &#123;</span><br><span class="line">        gOnStartHook(<span class="string">"RenderThread"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//初始化Thread的本地变量</span></span><br><span class="line">    initThreadLocals();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="comment">//没有任务就等在此处</span></span><br><span class="line">        waitForWork();</span><br><span class="line">        processQueue();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mPendingRegistrationFrameCallbacks.size() &amp;&amp; !mFrameCallbackTaskPending) &#123;</span><br><span class="line">            drainDisplayEventQueue();</span><br><span class="line">            mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),</span><br><span class="line">                                   mPendingRegistrationFrameCallbacks.end());</span><br><span class="line">            mPendingRegistrationFrameCallbacks.clear();</span><br><span class="line">            requestVsync();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!mFrameCallbackTaskPending &amp;&amp; !mVsyncRequested &amp;&amp; mFrameCallbacks.size()) &#123;</span><br><span class="line">            <span class="comment">// <span class="doctag">TODO:</span> Clean this up. This is working around an issue where a combination</span></span><br><span class="line">            <span class="comment">// of bad timing and slow drawing can result in dropping a stale vsync</span></span><br><span class="line">            <span class="comment">// on the floor (correct!) but fails to schedule to listen for the</span></span><br><span class="line">            <span class="comment">// next vsync (oops), so none of the callbacks are run.</span></span><br><span class="line">            requestVsync();</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="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h4 id="DrawFrameTask构造函数"><a href="#DrawFrameTask构造函数" class="headerlink" title="DrawFrameTask构造函数"></a>DrawFrameTask构造函数</h4><p>在<code>RenderProxy</code>调用构造函数时，会创建DrawFrameTask，同时调用其<code>setContext</code>函数：</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">DrawFrameTask::DrawFrameTask()</span><br><span class="line">        : mRenderThread(<span class="literal">nullptr</span>)</span><br><span class="line">        , mContext(<span class="literal">nullptr</span>)</span><br><span class="line">        , mContentDrawBounds(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>)</span><br><span class="line">        , mSyncResult(SyncResult::OK) &#123;&#125;</span><br><span class="line"></span><br><span class="line">DrawFrameTask::~DrawFrameTask() &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DrawFrameTask::setContext</span><span class="params">(RenderThread* thread, CanvasContext* context,</span></span></span><br><span class="line"><span class="function"><span class="params">                               RenderNode* targetNode)</span> </span>&#123;</span><br><span class="line">    mRenderThread = thread;</span><br><span class="line">    mContext = context;</span><br><span class="line">    mTargetNode = targetNode;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="syncAndDrawFrame绘制帧"><a href="#syncAndDrawFrame绘制帧" class="headerlink" title="syncAndDrawFrame绘制帧"></a>syncAndDrawFrame绘制帧</h3><ol><li>从上面的分析看，DisplayList和RenderThread都创建好了，正常绘制的时候会调用到<code>syncAndDrawFrame</code>：</li></ol><p>从ViewRootImpl的performDraw函数调用到draw，在调用到 ThreadedRenderer.java的draw函数。开始绘制：</p><figure class="highlight java"><figcaption><span>ThreadedRenderer.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">draw</span><span class="params">(View view, AttachInfo attachInfo, DrawCallbacks callbacks)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">final</span> Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;</span><br><span class="line">      choreographer.mFrameInfo.markDrawStart();</span><br><span class="line">      <span class="comment">//绘制每个视图的内容（在文章下面会梳理）</span></span><br><span class="line">      updateRootDisplayList(view, callbacks);</span><br><span class="line">      <span class="comment">//绘制一帧的内容</span></span><br><span class="line">      <span class="keyword">int</span> syncResult = syncAndDrawFrame(choreographer.mFrameInfo);</span><br><span class="line">      ...</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/RenderProxy.cpp</span></figcaption><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">int</span> <span class="title">RenderProxy::syncAndDrawFrame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> mDrawFrameTask.drawFrame();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">int</span> <span class="title">DrawFrameTask::drawFrame</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    LOG_ALWAYS_FATAL_IF(!mContext, <span class="string">"Cannot drawFrame with no CanvasContext!"</span>);</span><br><span class="line"></span><br><span class="line">    mSyncResult = SyncResult::OK;</span><br><span class="line">    mSyncQueued = systemTime(CLOCK_MONOTONIC);</span><br><span class="line">    postAndWait();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> mSyncResult;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DrawFrameTask::postAndWait</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    AutoMutex _lock(mLock);</span><br><span class="line">    mRenderThread-&gt;<span class="built_in">queue</span>().post([<span class="keyword">this</span>]() &#123; run(); &#125;); <span class="comment">//执行此处的run函数</span></span><br><span class="line">    mSignal.wait(mLock);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>此时，drawFrame，也就通过RenderThread，post一个WorkItem到RenderThread的队列里面，在RenderThread线程中执行的。</li></ol><p>然后RenderThread处理Queue时，执行的会是DrawFrameTask的run函数。</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DrawFrameTask::run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ATRACE_NAME(<span class="string">"DrawFrame"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> canUnblockUiThread;</span><br><span class="line">    <span class="keyword">bool</span> canDrawThisFrame;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//info，即描述Viewtree的，也就是RenderNode tree</span></span><br><span class="line">        <span class="comment">//此处的mode是MODE_FULL，即只有primary的node是FULL，其他都是实时 **</span></span><br><span class="line">        <span class="function">TreeInfo <span class="title">info</span><span class="params">(TreeInfo::MODE_FULL, *mContext)</span></span>;</span><br><span class="line">        <span class="comment">//同步Frame帧状态</span></span><br><span class="line">        canUnblockUiThread = syncFrameState(info);</span><br><span class="line">        <span class="comment">//判断是否可以绘制这一帧</span></span><br><span class="line">        canDrawThisFrame = info.out.canDrawThisFrame;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (mFrameCompleteCallback) &#123;</span><br><span class="line">            mContext-&gt;addFrameCompleteListener(<span class="built_in">std</span>::move(mFrameCompleteCallback));</span><br><span class="line">            mFrameCompleteCallback = <span class="literal">nullptr</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">// Grab a copy of everything we need</span></span><br><span class="line">    CanvasContext* context = mContext;</span><br><span class="line">    <span class="built_in">std</span>::function&lt;<span class="keyword">void</span>(<span class="keyword">int64_t</span>)&gt; callback = <span class="built_in">std</span>::move(mFrameCallback);</span><br><span class="line">    mFrameCallback = <span class="literal">nullptr</span>;</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> (CC_LIKELY(canDrawThisFrame)) &#123;</span><br><span class="line">        <span class="comment">//**绘制</span></span><br><span class="line">        context-&gt;draw();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// wait on fences so tasks don't overlap next frame</span></span><br><span class="line">        context-&gt;waitOnFences();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!canUnblockUiThread) &#123;</span><br><span class="line">        unblockUiThread();</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">//同步Frame的State</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">DrawFrameTask::syncFrameState</span><span class="params">(TreeInfo&amp; info)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    <span class="keyword">int64_t</span> vsync = mFrameInfo[<span class="keyword">static_cast</span>&lt;<span class="keyword">int</span>&gt;(FrameInfoIndex::Vsync)];</span><br><span class="line">    mRenderThread-&gt;timeLord().vsyncReceived(vsync);</span><br><span class="line">    <span class="comment">//通知GPU处理当前的Context上下文</span></span><br><span class="line">    <span class="keyword">bool</span> canDraw = mContext-&gt;makeCurrent();</span><br><span class="line">    <span class="comment">//hwui为了提高速度，对各种object都做了cache，此处unpin，就是让cache去做unpin，其他都不要了</span></span><br><span class="line">    mContext-&gt;unpinImages();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; mLayers.size(); i++) &#123;</span><br><span class="line">        mLayers[i]-&gt;apply();</span><br><span class="line">    &#125;</span><br><span class="line">    mLayers.clear();</span><br><span class="line">    <span class="comment">//设置绘制的区域大小</span></span><br><span class="line">    mContext-&gt;setContentDrawBounds(mContentDrawBounds);</span><br><span class="line">    <span class="comment">//**Android View是树型结构的，这就是在绘制之前，去准备这些Tree节点的绘图操作</span></span><br><span class="line">    <span class="comment">//准备绘制一帧的数据</span></span><br><span class="line">    mContext-&gt;prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// This is after the prepareTree so that any pending operations</span></span><br><span class="line">    <span class="comment">// (RenderNode tree state, prefetched layers, etc...) will be flushed.</span></span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(!mContext-&gt;hasSurface() || !canDraw)) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!mContext-&gt;hasSurface()) &#123;</span><br><span class="line">            mSyncResult |= SyncResult::LostSurfaceRewardIfFound;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// If we have a surface but can't draw we must be stopped</span></span><br><span class="line">            mSyncResult |= SyncResult::ContextIsStopped;</span><br><span class="line">        &#125;</span><br><span class="line">        info.out.canDrawThisFrame = <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (info.out.hasAnimations) &#123;</span><br><span class="line">        <span class="keyword">if</span> (info.out.requiresUiRedraw) &#123;</span><br><span class="line">            mSyncResult |= SyncResult::UIRedrawRequired;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!info.out.canDrawThisFrame) &#123;</span><br><span class="line">        mSyncResult |= SyncResult::FrameDropped;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// If prepareTextures is false, we ran out of texture cache space</span></span><br><span class="line">    <span class="keyword">return</span> info.prepareTextures;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h4 id="prepareTree"><a href="#prepareTree" class="headerlink" title="prepareTree"></a>prepareTree</h4><ol><li>调用函数<strong>prepareTree</strong>：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/CanvasContext.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">CanvasContext::prepareTree</span><span class="params">(TreeInfo&amp; info, <span class="keyword">int64_t</span>* uiFrameInfo, <span class="keyword">int64_t</span> syncQueued,</span></span></span><br><span class="line"><span class="function"><span class="params">                                RenderNode* target)</span> </span>&#123;</span><br><span class="line">    mRenderThread.removeFrameCallback(<span class="keyword">this</span>);</span><br><span class="line">......</span><br><span class="line">    mAnimationContext-&gt;startFrame(info.mode);</span><br><span class="line">    mRenderPipeline-&gt;onPrepareTree();</span><br><span class="line">    <span class="comment">//Context可能会有多个Node</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> sp&lt;RenderNode&gt;&amp; node : mRenderNodes) &#123;</span><br><span class="line">        <span class="comment">//即只有Primary的node是 FULL，其他都是实时</span></span><br><span class="line">        info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);</span><br><span class="line">        <span class="comment">//此处遍历，对每个RenderNode都进行prepare</span></span><br><span class="line">        node-&gt;prepareTree(info);</span><br><span class="line">        GL_CHECKPOINT(MODERATE);</span><br><span class="line">    &#125;</span><br><span class="line">    mAnimationContext-&gt;runRemainingAnimations(info);</span><br><span class="line">    GL_CHECKPOINT(MODERATE);</span><br><span class="line"></span><br><span class="line">    freePrefetchedLayers();</span><br><span class="line">    GL_CHECKPOINT(MODERATE);</span><br><span class="line"></span><br><span class="line">    mIsDirty = <span class="literal">true</span>;</span><br><span class="line">    <span class="comment">//如果窗口已经没有Native Surface，这一帧就丢掉！！</span></span><br><span class="line">    <span class="keyword">if</span> (CC_UNLIKELY(!hasSurface())) &#123;</span><br><span class="line">        mCurrentFrameInfo-&gt;addFlag(FrameInfoFlags::SkippedFrame);</span><br><span class="line">        info.out.canDrawThisFrame = <span class="literal">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">                                &#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>遍历RenderNode的prepareTree方法：</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/RenderNode.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">RenderNode::prepareTree</span><span class="params">(TreeInfo&amp; info)</span> </span>&#123;</span><br><span class="line">    ATRACE_CALL();</span><br><span class="line">    LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, <span class="string">"DamageAccumulator missing"</span>);</span><br><span class="line">    <span class="function">MarkAndSweepRemoved <span class="title">observer</span><span class="params">(&amp;info)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> before = info.disableForceDark;</span><br><span class="line">    <span class="comment">//具体实现</span></span><br><span class="line">    prepareTreeImpl(observer, info, <span class="literal">false</span>);</span><br><span class="line">    LOG_ALWAYS_FATAL_IF(before != info.disableForceDark, <span class="string">"Mis-matched force dark"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>prepareTreeImpl是RenderNode真正进行Prepare的地方。</li></ol><p>damageAccumulator是从CanvasContext中传过来的，是CanvasContext的成员，damage的叠加器。主要是用来标记，屏幕的那些区域被破坏了，需要重新绘制，所有的RenderNode累加起来，就是总的。</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/RenderNode.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">RenderNode::prepareTreeImpl</span><span class="params">(TreeObserver&amp; observer, TreeInfo&amp; info, <span class="keyword">bool</span> functorsNeedLayer)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mDamageGenerationId == info.damageGenerationId) &#123;</span><br><span class="line">        info.damageAccumulator-&gt;dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//Step 1</span></span><br><span class="line">    info.damageAccumulator-&gt;pushTransform(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (info.mode == TreeInfo::MODE_FULL) &#123;</span><br><span class="line">        <span class="comment">//property是对RenderNode的描述，也就是对View的描述，比如大小，位置等。</span></span><br><span class="line">        <span class="comment">//有两个状态，正在使用的syncProperties和待处理的mStagingProperties。</span></span><br><span class="line">        <span class="comment">//syncProperties时，将mStagingProperties赋值给syncProperties</span></span><br><span class="line">        pushStagingPropertiesChanges(info);</span><br><span class="line">    &#125;</span><br><span class="line">......</span><br><span class="line">    pushLayerUpdate(info);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!mProperties.getAllowForceDark()) &#123;</span><br><span class="line">        info.disableForceDark--;</span><br><span class="line">    &#125;</span><br><span class="line">    info.damageAccumulator-&gt;popTransform();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>调用pushTransform函数，damage累加器中，每一个元素由DirtyStack描述，分两种类型：TransformMatrix4和TransformRenderNode。采用一个双向链表mHead进行管理。</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/DamageAccumulator.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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">DamageAccumulator::pushCommon</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!mHead-&gt;next) &#123;</span><br><span class="line">        DirtyStack* nextFrame = mAllocator.create_trivial&lt;DirtyStack&gt;();</span><br><span class="line">        nextFrame-&gt;next = <span class="literal">nullptr</span>;</span><br><span class="line">        nextFrame-&gt;prev = mHead;</span><br><span class="line">        mHead-&gt;next = nextFrame;</span><br><span class="line">    &#125;</span><br><span class="line">    mHead = mHead-&gt;next;</span><br><span class="line">    mHead-&gt;pendingDirty.setEmpty();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">DamageAccumulator::pushTransform</span><span class="params">(<span class="keyword">const</span> RenderNode* transform)</span> </span>&#123;</span><br><span class="line">    pushCommon();</span><br><span class="line">    mHead-&gt;type = TransformRenderNode;</span><br><span class="line">    mHead-&gt;renderNode = transform;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时prepare完成后，绘制一帧的数据就准备好了。</p><hr><h3 id="绘制帧数据"><a href="#绘制帧数据" class="headerlink" title="绘制帧数据"></a>绘制帧数据</h3><ol><li>从<code>syncAndDrawFrame绘制帧</code>上面的章节，此处会调用到HardwareRenderer.java父类函数；</li><li>然后JNI到Native层，调用到RenderProxy.cpp的该函数，</li><li>再到<code>frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp</code>的drawFrame函数，开始绘制一帧数据，</li><li>此时启动RenderThread线程的run函数，调用到关键函数<code>CanvasContext::draw()</code></li></ol><p>Android Q中，具体绘制是在各自的pipeline中进行的。（在<code>frameworks/base/libs/hwui/pipeline/</code>）</p><p>在Android P中，一般是执行的<code>frameworks/base/libs/hwui/renderthread/OpenGLPipeline.cpp</code></p><p>在Android Q中，可以看出Google在慢慢用Vulkan替代OpenGL。</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/renderthread/CanvasContext.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">void</span> <span class="title">CanvasContext::draw</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    SkRect dirty;</span><br><span class="line">    mDamageAccumulator.finish(&amp;dirty);</span><br><span class="line">    <span class="comment">//跳过绘制条件：脏区域为空、空帧数据、不需要重绘</span></span><br><span class="line">    <span class="keyword">if</span> (dirty.isEmpty() &amp;&amp; Properties::skipEmptyFrames &amp;&amp; !surfaceRequiresRedraw()) &#123;</span><br><span class="line">        mCurrentFrameInfo-&gt;addFlag(FrameInfoFlags::SkippedFrame);</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">    mCurrentFrameInfo-&gt;markIssueDrawCommandsStart();</span><br><span class="line">    <span class="comment">//获取frame(一帧数据信息，主要是ufferAge、Surface等)</span></span><br><span class="line">    Frame frame = mRenderPipeline-&gt;getFrame();</span><br><span class="line">    setPresentTime();</span><br><span class="line"></span><br><span class="line">    SkRect windowDirty = computeDirtyRect(frame, &amp;dirty);</span><br><span class="line">    <span class="comment">//绘制</span></span><br><span class="line">    <span class="keyword">bool</span> drew = mRenderPipeline-&gt;draw(frame, windowDirty, dirty, mLightGeometry, &amp;mLayerUpdateQueue,</span><br><span class="line">                                      mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,</span><br><span class="line">                                      &amp;(profiler()));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int64_t</span> frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">    waitOnFences();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> requireSwap = <span class="literal">false</span>;</span><br><span class="line">    <span class="comment">//绘制完成后调用</span></span><br><span class="line">    <span class="keyword">bool</span> didSwap =</span><br><span class="line">            mRenderPipeline-&gt;swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &amp;requireSwap);</span><br><span class="line">......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="绘制完成后unlockCanvasAndPost流程"><a href="#绘制完成后unlockCanvasAndPost流程" class="headerlink" title="绘制完成后unlockCanvasAndPost流程"></a>绘制完成后unlockCanvasAndPost流程</h3><p>回到绘制案例，此时，RenderThread，DrawFrameTask，CanvasContext等已经就绪，绘制操纵已经被添加到了DisplayList中。</p><ol><li>绘制完成，然后会在<code>frameworks/base/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java</code>中调用到<code>方法unlockCanvasAndPost</code>。</li></ol><p>SurfaceHolder直接调的Surface的unlockCanvasAndPost。</p><figure class="highlight cpp"><figcaption><span>frameworks/base/core/java/android/view/Surface.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlockCanvasAndPost</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">        synchronized (mLock) &#123;</span><br><span class="line">            checkNotReleasedLocked();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (mHwuiContext != null) &#123;</span><br><span class="line">                <span class="comment">//硬件加速执行此处</span></span><br><span class="line">                mHwuiContext.unlockAndPost(canvas);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">//软件绘制</span></span><br><span class="line">                unlockSwCanvasAndPost(canvas);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">HwuiContext</span> &#123;</span></span><br><span class="line">    ......</span><br><span class="line">        <span class="comment">//HwuiContext的unlockAndPost函数</span></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">unlockAndPost</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (canvas != mCanvas) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"canvas object must be the same instance that "</span></span><br><span class="line">                        + <span class="string">"was previously returned by lockCanvas"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            mRenderNode.endRecording();</span><br><span class="line">            mCanvas = null;</span><br><span class="line">            nHwuiDraw(mHwuiRenderer);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>上面代码看到，在lockHardwareCanvas的时候有相同流程，会调用<code>mRenderNode.beginRecording</code>。</li></ol><p>此处就对应的调用到<code>mRenderNode.endRecording();</code>，结束RenderNode，保存数据。</p><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/RenderNode.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">public</span> <span class="keyword">void</span> <span class="title">endRecording</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (mCurrentRecordingCanvas == <span class="keyword">null</span>) &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(</span><br><span class="line">                   <span class="string">"No recording in progress, forgot to call #beginRecording()?"</span>);</span><br><span class="line">       &#125;</span><br><span class="line">       RecordingCanvas canvas = mCurrentRecordingCanvas;</span><br><span class="line">       mCurrentRecordingCanvas = <span class="keyword">null</span>;</span><br><span class="line">       <span class="comment">//Step1 先结束Canvas的录制</span></span><br><span class="line">       <span class="keyword">long</span> displayList = canvas.finishRecording();</span><br><span class="line">       <span class="comment">//Step2 然后将录制的list给mNativeRenderNode</span></span><br><span class="line">       nSetDisplayList(mNativeRenderNode, displayList);</span><br><span class="line">       canvas.recycle();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>首先finishRecording函数通过JIN层<code>android_view_DisplayListCanvas.cpp</code>调用到Native层。</li></ol><p>返回录制好的mDisplayList。</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="function">uirenderer::DisplayList* <span class="title">SkiaRecordingCanvas::finishRecording</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// close any existing chunks if necessary</span></span><br><span class="line">    insertReorderBarrier(<span class="literal">false</span>);</span><br><span class="line">    mRecorder.restoreToCount(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> mDisplayList.release();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>第二步的nSetDisplayList通过JNI层<code>android_view_RenderNode.cpp</code>调用到Native层。</li></ol><p>将displayList给到RenderNode的mStagingDisplayList。</p><figure class="highlight cpp"><figcaption><span>frameworks/base/libs/hwui/RenderNode.cpp</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="function"><span class="keyword">void</span> <span class="title">RenderNode::setStagingDisplayList</span><span class="params">(DisplayList* displayList)</span> </span>&#123;</span><br><span class="line">    mValid = (displayList != <span class="literal">nullptr</span>);</span><br><span class="line">    mNeedsDisplayListSync = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">delete</span> mStagingDisplayList;</span><br><span class="line">    mStagingDisplayList = displayList;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="Activity-HWUI渲染环境初始化流程（RenderThreadc创建）"><a href="#Activity-HWUI渲染环境初始化流程（RenderThreadc创建）" class="headerlink" title="Activity HWUI渲染环境初始化流程（RenderThreadc创建）"></a>Activity HWUI渲染环境初始化流程（RenderThreadc创建）</h2><blockquote><p>主要是通过setView创建rendernode，渲染线程RenderThread，Context上下文，RenderProxy代理对象等。</p></blockquote><h3 id="附序列图"><a href="#附序列图" class="headerlink" title="附序列图"></a>附序列图</h3><p><img src="HWUI_Initialize_RenderThreadCreate.png" alt="Android HWUI环境初始化 - 创建RenderThread"></p><h3 id="Java层-1"><a href="#Java层-1" class="headerlink" title="Java层"></a>Java层</h3><ol><li>Activity.java开始设置view：</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/app/Activity.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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> Window <span class="title">getWindow</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mWindow;</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">setContentView</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">        getWindow().setContentView(view);</span><br><span class="line">        initWindowDecorActionBar();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>PhoneWindow继承Window抽象类调用setContentView函数：</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">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="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">(View view)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//新对象ViewGroup</span></span><br><span class="line">        setContentView(view, <span class="keyword">new</span> ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));</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">setContentView</span><span class="params">(View view, ViewGroup.LayoutParams params)</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">            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">            view.setLayoutParams(params);</span><br><span class="line">            <span class="keyword">final</span> Scene newScene = <span class="keyword">new</span> Scene(mContentParent, view);</span><br><span class="line">            transitionTo(newScene);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//调用此处添加一个view到Parent View中</span></span><br><span class="line">            mContentParent.addView(view, params);</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><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处的<code>mContentParent.addView(view, params);</code>，mContentParent是ViewGroup类型对象。</p><p>ViewGroup实现接口ViewManager，而<code>interface WindowManager extends ViewManager</code>，WindowManagerImpl.java又是接口WindowManager的实现类。所以会同时调用WindowManagerImpl类的addView函数。</p><ol start="3"><li>WindowManagerImpl.java调用addView函数。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/WindowManagerImpl.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">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><ol start="4"><li>调用windowManagerGlobal的addView函数。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/WindowManagerGlobal.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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">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">    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><br><span class="line">        <span class="comment">// do this last because it fires off messages to start doing things</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//调用ViewRootImpl的setView函数</span></span><br><span class="line">            root.setView(view, wparams, panelParentView);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RuntimeException e) &#123;</span><br><span class="line">            <span class="comment">// BadTokenException or InvalidDisplayException, clean up.</span></span><br><span class="line">            <span class="keyword">if</span> (index &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                removeViewLocked(index, <span class="keyword">true</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ol start="5"><li>ViewRootImpl.java中调用流程：</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/ViewRootImpl.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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"> * We have one child</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">setView</span><span class="params">(View view, WindowManager.LayoutParams attrs, View panelParentView)</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> (mView == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">//设置根节点视图（顶层视图）</span></span><br><span class="line">            mView = view;</span><br><span class="line">            ...</span><br><span class="line">            <span class="comment">// If the application owns the surface, don't enable hardware acceleration</span></span><br><span class="line">            <span class="keyword">if</span> (mSurfaceHolder == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="comment">// While this is supposed to enable only, it can effectively disable</span></span><br><span class="line">                <span class="comment">// the acceleration too.</span></span><br><span class="line">                <span class="comment">//启动硬件加速！</span></span><br><span class="line">                enableHardwareAcceleration(attrs);</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">boolean</span> useMTRenderer = MT_RENDERER_AVAILABLE</span><br><span class="line">                        &amp;&amp; mAttachInfo.mThreadedRenderer != <span class="keyword">null</span>;</span><br><span class="line">                <span class="keyword">if</span> (mUseMTRenderer != useMTRenderer) &#123;</span><br><span class="line">                    <span class="comment">// Shouldn't be resizing, as it's done only in window setup,</span></span><br><span class="line">                    <span class="comment">// but end just in case.</span></span><br><span class="line">                    endDragResizing();</span><br><span class="line">                    mUseMTRenderer = useMTRenderer;</span><br><span class="line">            ...</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@UnsupportedAppUsage</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">enableHardwareAcceleration</span><span class="params">(WindowManager.LayoutParams attrs)</span> </span>&#123;</span><br><span class="line">        ...</span><br><span class="line">        mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,</span><br><span class="line">                    attrs.getTitle().toString());</span><br><span class="line">        ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>在ThreadedRenderer的create函数中new了一个该对象，从而调用其构造函数。构造函数的<code>super()</code>调用基类HardwareRenderer的构造函数。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/graphics/java/android/graphics/HardwareRenderer.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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"> * Creates a new instance of a HardwareRenderer. The HardwareRenderer will default</span></span><br><span class="line"><span class="comment"> * to opaque with no light source configured.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">//创建硬件渲染rendernode对象</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">HardwareRenderer</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//创建一个窗口的Root Render Node，并用Java层的RenderNode封装起来，即adopt通过new对象返回一个java层的RenderNode对象</span></span><br><span class="line">    mRootNode = RenderNode.adopt(nCreateRootRenderNode());</span><br><span class="line">    mRootNode.setClipToBounds(<span class="keyword">false</span>);</span><br><span class="line">    <span class="comment">//调用到JNI层，创建一个RenderProxy（即MainThread的代理对象）</span></span><br><span class="line">    mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);</span><br><span class="line">    <span class="keyword">if</span> (mNativeProxy == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> OutOfMemoryError(<span class="string">"Unable to create hardware renderer"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    Cleaner.create(<span class="keyword">this</span>, <span class="keyword">new</span> DestroyContextRunnable(mNativeProxy));</span><br><span class="line">    ProcessInitializer.sInstance.init(mNativeProxy);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Native层"><a href="#Native层" class="headerlink" title="Native层"></a>Native层</h3><ol><li>从上面的nCreateRootRenderNode函数调用到JNI层的android_view_ThreadedRenderer.cpp文件中。</li></ol><p>创建一个窗口的Root Render Node。</p><p>之后创建RenderProxy对象。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> jlong <span class="title">android_view_ThreadedRenderer_createRootRenderNode</span><span class="params">(JNIEnv* env, jobject clazz)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//new对象</span></span><br><span class="line">    RootRenderNode* node = <span class="keyword">new</span> RootRenderNode(env);</span><br><span class="line">    node-&gt;incStrong(<span class="number">0</span>);</span><br><span class="line">    node-&gt;setName(<span class="string">"RootRenderNode"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">reinterpret_cast</span>&lt;jlong&gt;(node);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> jlong <span class="title">android_view_ThreadedRenderer_createProxy</span><span class="params">(JNIEnv* env, jobject clazz,</span></span></span><br><span class="line"><span class="function"><span class="params">        jboolean translucent, jlong rootRenderNodePtr)</span> </span>&#123;</span><br><span class="line">    RootRenderNode* rootRenderNode = <span class="keyword">reinterpret_cast</span>&lt;RootRenderNode*&gt;(rootRenderNodePtr);</span><br><span class="line">    <span class="function">ContextFactoryImpl <span class="title">factory</span><span class="params">(rootRenderNode)</span></span>;</span><br><span class="line">    <span class="comment">//new对象</span></span><br><span class="line">    <span class="keyword">return</span> (jlong) <span class="keyword">new</span> RenderProxy(translucent, rootRenderNode, &amp;factory);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>之后调用RenderProxy构造函数，就同上面Google原生绘制案例的流程分析相同<code>RenderProxy::RenderProxy(){...}</code>，负责从MainThread向RenderThread发送命令。</li></ol><p><strong>参考上面的<code>native层：调用RenderProxy.cpp构造函数</code>和<code>CanvasContext渲染上下文</code>章节</strong></p><h3 id="ThreadedRenderer架构图"><a href="#ThreadedRenderer架构图" class="headerlink" title="ThreadedRenderer架构图"></a>ThreadedRenderer架构图</h3><p><img src="ThreadedRenderer.jpg" alt="ThreadedRenderer和底层结构关系"></p><hr><h2 id="Activity-窗口绘制流程"><a href="#Activity-窗口绘制流程" class="headerlink" title="Activity 窗口绘制流程"></a>Activity 窗口绘制流程</h2><blockquote><p>由ViewRootImpl的成员函数<code>performTraversals</code>发起。</p></blockquote><ol><li>在绘制之前，首先需要创建一个Surface，即描述一个窗口（创建Surface的流程后续单独整理）</li><li>一旦获得对应的Surface， 就需要将其绑定到RenderThread中。</li><li>Activity窗口对应的Surface是通过ViewRootImpl类的成员函数<code>relayoutWindow</code>向WindowManagerService服务请求创建和返回的，并且保存在ViewRootImpl类的成员变量mSurface中。</li><li>如果Surface是新创建的，将调用ViewRootImpl类的成员变量mAttachInfo指向的AttachInfo函数。对象的成员变量mHardwareRenderer描述的一个HardwareRenderer对象的成员函数<code>initialize</code>将它绑定到RenderThread中。</li><li>最后, 如果需要绘制当前的Activity窗口, 那会调用 iewRootImpl类的另外一个成员函数<code>performDraw</code></li></ol><blockquote><p>此处在performTraversals函数中，先是用relayoutWindow创建mSurface，将Native层之前通过构造函数创建的Surface copy过来。<br>而后是三个主要流程：performMeasure测量、performLayout布局、performDraw绘制</p></blockquote><h3 id="绘制序列图"><a href="#绘制序列图" class="headerlink" title="绘制序列图"></a>绘制序列图</h3><p><img src="%E7%A1%AC%E4%BB%B6%E6%B8%B2%E6%9F%93%E9%A1%BA%E5%BA%8F%E5%9B%BE.png" alt="Android HWUI窗口绘制"></p><h3 id="ViewRootImpl中的软-硬件绘制区分"><a href="#ViewRootImpl中的软-硬件绘制区分" class="headerlink" title="ViewRootImpl中的软/硬件绘制区分"></a>ViewRootImpl中的软/硬件绘制区分</h3><p>在ViewRootImpl.java的draw函数中：</p><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/ViewRootImpl.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">draw</span><span class="params">(<span class="keyword">boolean</span> fullRedrawNeeded)</span> </span>&#123;</span><br><span class="line">        Surface surface = mSurface;</span><br><span class="line">        <span class="keyword">if</span> (!surface.isValid()) &#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">// Draw with hardware renderer.</span></span><br><span class="line">        mIsAnimating = <span class="keyword">false</span>;</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">if</span> (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) &#123;</span><br><span class="line">            <span class="keyword">if</span> (mAttachInfo.mThreadedRenderer != <span class="keyword">null</span> &amp;&amp; mAttachInfo.mThreadedRenderer.isEnabled()) &#123;</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">//硬件加速</span></span><br><span class="line">        mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, <span class="keyword">this</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        ...</span><br><span class="line">                <span class="comment">//软件绘制</span></span><br><span class="line">                <span class="keyword">if</span> (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,</span><br><span class="line">                        scalingRequired, dirty, surfaceInsets)) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (DEBUG_DRAW) &#123;</span><br><span class="line">                        Log.v(mTag, <span class="string">"drawSoftware return: this = "</span> + <span class="keyword">this</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">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><p>drawSoftware中会调用到Surface.java的<code>lockCanvas</code>和<code>unlockCanvasAndPost</code>函数。</p><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/Surface.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Canvas <span class="title">lockCanvas</span><span class="params">(Rect inOutDirty)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> Surface.OutOfResourcesException, IllegalArgumentException </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mLock) &#123;</span><br><span class="line">            <span class="comment">/// M: add for white list @&#123;</span></span><br><span class="line">            <span class="comment">//如果在白名单，则返回，仍使用硬件绘制</span></span><br><span class="line">            <span class="keyword">if</span> (mSurfaceExt.isInWhiteList()) &#123;</span><br><span class="line">                <span class="keyword">return</span> lockHardwareCanvas();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">/// @&#125;</span></span><br><span class="line">            ...</span><br><span class="line">            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);</span><br><span class="line">            <span class="keyword">return</span> mCanvas;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">unlockSwCanvasAndPost</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            nativeUnlockCanvasAndPost(mLockedObject, canvas);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            nativeRelease(mLockedObject);</span><br><span class="line">            mLockedObject = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="初始化DisplayList"><a href="#初始化DisplayList" class="headerlink" title="初始化DisplayList"></a>初始化DisplayList</h3><ol><li>updateRootDisplayList方法的作用是先初始化DIsplayList（即调用<code>beginRecording</code>，流程同上面案例中的分析），然后绘制整个树型视图结构，从顶层视图开始，每个视图节点逐一绘制，最终目的是触发每个视图的<code>Canvas#draw***</code>方法。</li><li><code>syncAndDrawFrame</code>查看上面章节<code>绘制准备，同步帧</code></li></ol><p>updateRootDisplayList方法分成两步：</p><ol><li>先顶层视图结构遍历绘制，更新DisplayList数据，</li><li>第二步是ThreadedRenderer的根RenderNode绘制，同样，通过根RenderNode创建DisplayListCanvas，通过它的drawRenderNode方法，负责绘制顶层视图DecorView的RenderNode节点。</li></ol><figure class="highlight cpp"><figcaption><span>frameworks/base/core/java/android/view/ThreadedRenderer.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">draw</span><span class="params">(View view, AttachInfo attachInfo, DrawCallbacks callbacks)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;</span><br><span class="line">        choreographer.mFrameInfo.markDrawStart();</span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        updateRootDisplayList(view, callbacks);</span><br><span class="line">        ...</span><br><span class="line">        <span class="comment">//同步帧数据，最终目的OpenGL指令写入gpu</span></span><br><span class="line">        <span class="keyword">int</span> syncResult = syncAndDrawFrame(choreographer.mFrameInfo);</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">void</span> <span class="title">updateRootDisplayList</span><span class="params">(View view, DrawCallbacks callbacks)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//Step1 初始化DisplayList，从顶层视图开始，更新所有视图的DisplayList</span></span><br><span class="line">        updateViewTreeDisplayList(view);</span><br><span class="line">        <span class="comment">//Step2 根节点绘制顶层视图RenderNode</span></span><br><span class="line">        <span class="keyword">if</span> (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) &#123;</span><br><span class="line">            <span class="comment">//参考上面目录`beginRecording创建DisplayList`小节</span></span><br><span class="line">            RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="keyword">int</span> saveCount = canvas.save();</span><br><span class="line">                canvas.translate(mInsetLeft, mInsetTop);</span><br><span class="line">                callbacks.onPreDraw(canvas);</span><br><span class="line"></span><br><span class="line">                canvas.enableZ();</span><br><span class="line">                <span class="comment">//此处还会调用一次根View的updateDisplayListIfDirty方法，不会再进行一次View树绘制</span></span><br><span class="line">                <span class="comment">//这时的view还是DecorView，它的DisplayListCanvas已经end结束记录</span></span><br><span class="line">                <span class="comment">//并且，View的RenderNode节点mValid已有效，且mRecreateDisplayList标志已被恢复。</span></span><br><span class="line">                canvas.drawRenderNode(view.updateDisplayListIfDirty());</span><br><span class="line">                canvas.disableZ();</span><br><span class="line"></span><br><span class="line">                callbacks.onPostDraw(canvas);</span><br><span class="line">                canvas.restoreToCount(saveCount);</span><br><span class="line">                mRootNodeNeedsUpdate = <span class="literal">false</span>;</span><br><span class="line">            &#125; finally &#123;</span><br><span class="line">                mRootNode.endRecording();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        Trace.traceEnd(Trace.TRACE_TAG_VIEW);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="绘制view视图"><a href="#绘制view视图" class="headerlink" title="绘制view视图"></a>绘制view视图</h3><p><img src="ThreadedRender_Draw.jpg" alt="绘制视图"></p><blockquote><p>参考上面目录<code>beginRecording初始化DisplayList</code>小节</p></blockquote><ul><li>updateViewTreeDisplayList方法，从顶层视图DecorView开始，遍历树形视图结构的每一个节点，利用视图内的RenderNode创建Canvas，绘制。</li><li>利用ThreadedRenderer的根RootRenderNode创建Canvas，绘制顶层RenderNode节点</li></ul><figure class="highlight cpp"><figcaption><span>frameworks/base/core/java/android/view/ThreadedRenderer.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">private</span> <span class="keyword">void</span> <span class="title">updateViewTreeDisplayList</span><span class="params">(View view)</span> </span>&#123;</span><br><span class="line">     view.mPrivateFlags |= View.PFLAG_DRAWN;</span><br><span class="line">     <span class="comment">//判断视图的PFLAG_INVALIDATED标志</span></span><br><span class="line">     <span class="comment">//有这个标志，在调用每个View的updateDisplayListIfDirty()时，才会创建Canvas</span></span><br><span class="line">     <span class="comment">//当一个视图需要绘制时，上层肯定会设置该标志</span></span><br><span class="line">     <span class="comment">//最后会将重建标志还原</span></span><br><span class="line">     view.mRecreateDisplayList = (view.mPrivateFlags &amp; View.PFLAG_INVALIDATED)</span><br><span class="line">             == View.PFLAG_INVALIDATED;</span><br><span class="line">     view.mPrivateFlags &amp;= ~View.PFLAG_INVALIDATED;</span><br><span class="line">     <span class="comment">//此处会用到mRecreateDisplayList进行判断是否创建DisplayListCanvas</span></span><br><span class="line">     view.updateDisplayListIfDirty();</span><br><span class="line">     view.mRecreateDisplayList = <span class="literal">false</span>;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>每个视图的流程是一样的，都有三个步骤，第一次绘制时，每个视图都要建立Canvas。</p><ol><li>通过视图RenderNode节点start方法，创建DisplayListCanvas画布对象（调用<code>beginRecording</code>，流程同上面案例中的分析）</li><li>通过View的draw(canvas)方法，实现具体记录绘制操作，(绘制自身与派发)，draw方法包括很多步骤，包括递归到子视图的updateDisplayListIfDirty方法。</li><li>最后，RenderNode结束记录<code>endRecording</code>方法。</li></ol><h4 id="draw实现视图绘制六个步骤"><a href="#draw实现视图绘制六个步骤" class="headerlink" title="draw实现视图绘制六个步骤"></a>draw实现视图绘制六个步骤</h4><p>第二步的draw实现视图绘制。参数就是上面创建的DisplayListConvas画布，视图有一些公用绘制，例如背景，滚定条，修饰等。</p><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/View.java</span></figcaption><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">void</span> <span class="title">draw</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>具体视图绘制六个步骤：</strong></p><ol><li>绘制背景:<code>drawBackground(canvas)</code></li></ol><ul><li>这里会先得到一个mBGDrawable对象</li><li>然后根据layout过程确定的视图位置来设置背景的绘制区域</li><li>之后再调用Drawable的draw()方法来完成背景的绘制工作</li></ul><ol start="2"><li>必要时保存canvas的layers，绘制边缘fade</li><li>onDraw方法：绘制视图内容，调用Canvas API，<strong>此处是空方法，子类会实现。例如TextView、ImageView等类的源码，它们都有重写onDraw()这个方法，并且在里面执行了相当不少的绘制逻辑。绘制的方式主要是借助Canvas这个类，它会作为参数传入到onDraw()方法中，供给每个视图使用，可以将其当成一块画布</strong></li></ol><ul><li>下面有个APP代码案例</li></ul><ol start="4"><li>dispatchDraw派发绘制子视图，空方法，容器类视图会重写。如果有跳过标志，将不会来到draw方法，直接去dispatchDraw。</li></ol><figure class="highlight java"><figcaption><span>frameworks/base/core/java/android/view/ViewGroup.java</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">protected</span> <span class="keyword">void</span> <span class="title">dispatchDraw</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; childrenCount; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> ((child.mViewFlags &amp; VISIBILITY_MASK) == VISIBLE ||</span><br><span class="line">                child.getAnimation() != <span class="keyword">null</span>) &#123;</span><br><span class="line">            more |= drawChild(canvas, child, drawingTime);</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="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">drawChild</span><span class="params">(Canvas canvas, View child, <span class="keyword">long</span> drawingTime)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> child.draw(canvas, <span class="keyword">this</span>, drawingTime);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处调用View.java的三个参数的draw函数中，也会调用<code>updateDisplayListIfDirty()</code>函数。并且如果是硬件绘制会重新判断<code>mRecreateDisplayList</code>。</p><p>这样就实现了View视图的递归绘制。</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="function"><span class="keyword">boolean</span> <span class="title">draw</span><span class="params">(Canvas canvas, ViewGroup parent, <span class="keyword">long</span> drawingTime)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">        <span class="keyword">if</span> (hardwareAcceleratedCanvas) &#123;</span><br><span class="line">            <span class="comment">// Clear INVALIDATED flag to allow invalidation to occur during rendering, but</span></span><br><span class="line">            <span class="comment">// retain the flag's value temporarily in the mRecreateDisplayList flag</span></span><br><span class="line">            mRecreateDisplayList = (mPrivateFlags &amp; PFLAG_INVALIDATED) != <span class="number">0</span>;</span><br><span class="line">            mPrivateFlags &amp;= ~PFLAG_INVALIDATED;</span><br><span class="line">        &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="5"><li>如有绘制fading edges，恢复canvas’ layers。</li><li>绘制修饰，如滚动条。</li></ol><hr><h4 id="APP绘制案例代码"><a href="#APP绘制案例代码" class="headerlink" title="APP绘制案例代码"></a>APP绘制案例代码</h4><ol><li>假如APP代码中创建一个非常简单的视图，并且用Canvas随便绘制了一点东西，代码如下所示：</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></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">MyView</span> <span class="keyword">extends</span> <span class="title">View</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Paint mPaint;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyView</span><span class="params">(Context context, AttributeSet attrs)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context, attrs);</span><br><span class="line">        mPaint = <span class="keyword">new</span> Paint(Paint.ANTI_ALIAS_FLAG);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onDraw</span><span class="params">(Canvas canvas)</span> </span>&#123;</span><br><span class="line">        mPaint.setColor(Color.YELLOW);</span><br><span class="line">        canvas.drawRect(<span class="number">0</span>, <span class="number">0</span>, getWidth(), getHeight(), mPaint);</span><br><span class="line">        mPaint.setColor(Color.BLUE);</span><br><span class="line">        mPaint.setTextSize(<span class="number">20</span>);</span><br><span class="line">        String text = <span class="string">"Hello View"</span>;</span><br><span class="line">        canvas.drawText(text, <span class="number">0</span>, getHeight() / <span class="number">2</span>, mPaint);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此处创建了一个自定义的MyView继承自View，并在MyView的构造函数中创建了一个Paint对象。Paint就像是一个画笔一样，配合着Canvas就可以进行绘制。</p><p>绘制逻辑比较简单，在<code>onDraw()</code>方法中先是把画笔设置成黄色，然后调用Canvas的drawRect()方法绘制一个矩形。然后在把画笔设置成蓝色，并调整了一下文字的大小，然后调用drawText()方法绘制了一段文字。</p><ol start="2"><li>然后在XML布局假如这个视图，将MyView的宽度设置成200dp，高度设置成100dp。</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">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> &gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">com.example.diyview.MyView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"200dp"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"100dp"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">LinearLayout</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="关闭硬件加速"><a href="#关闭硬件加速" class="headerlink" title="关闭硬件加速"></a>关闭硬件加速</h2><p>android提供了以下四个级别的硬件加速控制：</p><ul><li>Application：<code>&lt;application android:hardwareAccelerated=&quot;true&quot; ...&gt;</code></li><li>Activity：例如启用全局的硬件加速，但却禁止了一个Activity的硬件加速：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">application</span> <span class="attr">android:hardwareAccelerated</span>=<span class="string">"true"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">activity</span> <span class="attr">...</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:hardwareAccelerated</span>=<span class="string">"false"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">application</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>Window：针对给定的Window来启用硬件加速：</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">getWindow().setFlags(</span><br><span class="line">    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,</span><br><span class="line">    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);</span><br></pre></td></tr></table></figure><ul><li>View级别：针对一个独立的View对象使用下列代码来禁止硬件加速：<code>myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);</code></li></ul><p><strong>android关闭硬件加速的方法：</strong></p><ol><li>APK中，在AndroidManifest.xml中设置<code>android:hardwareAccelerated=&quot;false&quot;</code>，这是关闭整个app的硬件加速，慎用！</li><li>View有个方法支持单独的View关闭硬件加速，可以设置<code>mView.setLaterType(View.LAYER_TYPE_SOFTWARE);</code>，或者关闭某一个控件的硬件加速功能使用<code>findViewById(R.id.btn).setLayerType(View.LAYER_TYPE_SOFTWARE,null);</code></li></ol><hr><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>在Java层，硬件渲染由ThreadedRenderer负责，每个窗体根视图ViewRootImpl下都有一个ThreadedRenderer，保存在AttachInfo，<code>它的draw方法是硬件渲染绘制的入口</code>。</li><li>从ViewRootImpl开始，一般视图会创建ThreadedRenderer，启用硬件渲染，关键点在遍历每一个视图，根据视图RenderNode创建画布，<code>有效绘制记录存储在RenderNode关联的底层DisplayListData</code></li><li>绘制架构包含<code>RenderNode节点，DisplayListCanvas画布，底层DisplayListData对象，CanvasState状态存储对象</code>，做完这些初始化工作，就可以在Java层画布上执行绘制操作方法。<code>树形视图结构每一节点都有一个DisplayListCanvas，利用Canvas#drawXxx方法分别记录一些绘制操作，drawXxx画点、圆、矩形等操作，将这些操作存储在一个DisplayList集合中，这是App的UI线程负责的任务</code></li><li><code>onDraw方法自己在APP中重写</code>，View和ViewGroup有什么自己需要的绘制在这里完成。</li><li>View的dispatchDraw是空方法，不做任何操作。ViewGroup重写dispatchDraw方法，实现绘制派发到子视图。容器视图一般没有自己要绘制的东西，可能在updateDisplayListIfDirty方法就已经选择dispatchDraw了；</li><li>顶层视图绘制入口是draw(一个参数)方法，在draw(一个参数)中，包含六个步骤，第四步会派发每个子视图，子视图绘制入口是draw(三个参数)，在draw(三个参数)中，会根据硬件渲染，进入每个子视图updateDisplayListIfDirty方法，实现递归绘制；</li><li>当走到RenderNode的<code>endRecording</code>方法时，表示视图本身以及子视图已经全部绘制完毕，也就是说当DecorView的RenderNode#end方准备执行时，所有draw已经完成</li><li>View构造方法创建每一个视图的RenderNode。每一个RenderNode都会创建DisplayListCanvas，使用时是一一对应关系；</li></ol><h2 id="相关参考"><a href="#相关参考" class="headerlink" title="相关参考"></a>相关参考</h2><ul><li>Android Q AOSP： <a href="http://aosp.opersys.com/xref/android-10.0.0_r39/" target="_blank" rel="noopener">http://aosp.opersys.com/xref/android-10.0.0_r39/</a></li><li>Android P 图像显示系统（三）Android HWUI 绘制流程： <a href="https://www.jianshu.com/p/abfaea892611" target="_blank" rel="noopener">https://www.jianshu.com/p/abfaea892611</a></li><li>硬件渲染一绘制阶段上层基本流程：<a href="https://www.jianshu.com/p/85d38ef937e9" target="_blank" rel="noopener">https://www.jianshu.com/p/85d38ef937e9</a></li><li>Android视图绘制流程之onDraw()：<a href="https://www.jianshu.com/p/f0f44cd58711" target="_blank" rel="noopener">https://www.jianshu.com/p/f0f44cd58711</a></li><li>Android硬件加速（译文）：<a href="https://www.jianshu.com/p/601a21b00475" target="_blank" rel="noopener">https://www.jianshu.com/p/601a21b00475</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Android中绘图的API有很多，比如2D的绘图skia；3D的绘图OpenGLES，Vulkan等。Android在后来完善3D API支持的同时，也在更新View Widget渲染机制，提出了硬件加速机制。  &lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="graphics" scheme="https://alonealive.github.io/Blog/tags/graphics/"/>
    
  </entry>
  
  <entry>
    <title>Android ANR traces.txt文件分析</title>
    <link href="https://alonealive.github.io/Blog/2020/06/11/2020/200611_android_tracetxt/"/>
    <id>https://alonealive.github.io/Blog/2020/06/11/2020/200611_android_tracetxt/</id>
    <published>2020-06-11T15:52:00.000Z</published>
    <updated>2020-07-22T12:03:21.223Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><code>trace.txt</code>生成:当APP(包括系统APP和用户APP)进程出现ANR、应用响应慢或WatchDog的监视没有得到回馈时,系统会dump此时的top进程,进程中Thread的运行状态就都dump到这个Trace文件中了。</p></blockquote><a id="more"></a><p>ANR:Application Not Responding，即应用无响应</p><h2 id="ANR类型"><a href="#ANR类型" class="headerlink" title="ANR类型"></a>ANR类型</h2><p>一般有三种类型:</p><ol><li>KeyDispatchTimeout(5 seconds) –主要类型：按键或触摸事件在特定时间内无响应</li><li>BroadcastTimeout(10 seconds)  –BroadcastReceiver：在特定时间内无法处理完成</li><li>ServiceTimeout(20 seconds) –小概率类型：Service在特定的时间内无法处理完成</li></ol><p>另外还有<code>ProviderTimeout</code>和<code>WatchDog看门狗</code>等导致的ANR。</p><p>还有当系统内存或CPU资源不足时容易出现ANR， 一般这种情况会有<code>lowmemorykill</code>的log打印。</p><p>应用ANR产生的时候，在<code>ActivityManagerService</code>中会调用<code>appNotResponding</code>方法, 然后在<code>/data/anr/traces.txt</code>文件中写入ANR相关信息。</p><h2 id="trace-txt获取"><a href="#trace-txt获取" class="headerlink" title="trace.txt获取"></a>trace.txt获取</h2><ol><li><code>adb shell</code>进入手机的<code>/data/anr</code>文件目录下面查看生成的<code>trace.txt</code>文件(如果<code>ls</code>查看文件列表没有权限,可以先<code>adb root</code>一下)</li><li><code>adb pull /data/anr/</code> 将该文件导出,然后分析</li></ol><p>log打印了ANR的基本信息(<code>adb shell top</code>查看进程, <code>adb logcat -v process |grep PID</code>查看日志), 可以分析<code>CPU使用率</code>得知ANR的简单情况;</p><p>如果CPU使用率很高,接近100%,可能是在进行大规模的计算更可能是陷入死循环;</p><p>如果CUP使用率很低,说明主线程被阻塞了,并且当IOwait很高,可能是主线程在等待I/O操作的完成。</p><p>对于ANR只是分析Log， 很难知道问题所在,我们还需要通过<code>Trace文件分析stack调用情况</code>,在log中显示的pid在traces文件中与之对应, 然后通过查看堆栈调用信息分析ANR的代码。</p><p>注:trace 文件的分析参考 <a href="https://blog.csdn.net/qq_25804863/article/details/49111005" target="_blank" rel="noopener">https://blog.csdn.net/qq_25804863/article/details/49111005</a></p><hr><h2 id="Trace分析"><a href="#Trace分析" class="headerlink" title="Trace分析"></a>Trace分析</h2><p>Traces中显示的线程状态都是C代码定义的，可以通过查看线程状态对应的信息分析ANR问题。</p><p>如:</p><ul><li><code>TimedWaiting</code>对应的线程状态是TIMED_WAITING；</li><li><code>kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout</code>执行了无超时参数的wait函数；</li><li><code>kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep()</code>执行了带有超时参数的 sleep 函数；</li><li>ZOMBIE                    线程死亡,终止运行</li><li>RUNNING/RUNNABLE          线程可运行或正在运行</li><li>TIMED_WAIT                执行了带有超时参数的 wait、sleep 或 join 函数</li><li>MONITOR                   线程阻塞,等待获取对象锁</li><li>WAIT                      执行了无超时参数的 wait 函数</li><li>INITIALIZING              新建,正在初始化,为其分配资源</li><li>STARTING                  新建,正在启动</li><li>NATIVE                    正在执行 JNI 本地函数</li><li>VMWAIT                    正在等待 VM 资源</li><li>SUSPENDED                 线程暂停,通常是由于 GC 或 debug 被暂停</li></ul>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;trace.txt&lt;/code&gt;生成:当APP(包括系统APP和用户APP)进程出现ANR、应用响应慢或WatchDog的监视没有得到回馈时,系统会dump此时的top进程,进程中Thread的运行状态就都dump到这个Trace文件中了。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="android" scheme="https://alonealive.github.io/Blog/categories/android/"/>
    
    
      <category term="display" scheme="https://alonealive.github.io/Blog/tags/display/"/>
    
  </entry>
  
</feed>
