<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>persilee&#39;s Blog</title>
    <link>https://h.lishaoy.net/</link>
    <atom:link href="/rss2.xml" rel="self" type="application/rss+xml"/>
    
    <description>李少颖的博客</description>
    <pubDate>Thu, 04 Mar 2021 18:13:57 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>Flutter 使用 Riverpod+Retrofit 构建MVVM开发模式</title>
      <link>https://h.lishaoy.net/flutterMVVM.html</link>
      <guid>https://h.lishaoy.net/flutterMVVM.html</guid>
      <pubDate>Tue, 23 Feb 2021 15:51:54 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/image/flutterMVVM/cover.png&quot; class=&quot;full-image&quot; alt=&quot;flutter app&quot; title=&quot;flutter app&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;最近，在使用 &lt;strong&gt;Flutter&lt;/strong&gt; 做一个图片分享的应用，自己创建出一套 &lt;strong&gt;Flutter&lt;/strong&gt; 版的 &lt;strong&gt;MVVM&lt;/strong&gt; 开发模式，觉得还挺好用，所以在此分享出来。&lt;/p&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/image/flutterMVVM/cover.png" class="full-image" alt="flutter app" title="flutter app"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>最近，在使用 <strong>Flutter</strong> 做一个图片分享的应用，自己创建出一套 <strong>Flutter</strong> 版的 <strong>MVVM</strong> 开发模式，觉得还挺好用，所以在此分享出来。</p><a id="more"></a><h2 id="应用功能展示"><a href="#应用功能展示" class="headerlink" title="应用功能展示"></a>应用功能展示</h2><p>首先，我们来看看我们这套MVVM开发模式，开发出来的应用是个什么样子，大概的一部分功能如下：(也可以点击观看 <a href="https://www.bilibili.com/video/BV1ur4y1A7of" target="_blank" rel="noopener">演示视频</a>)</p><p>下拉刷新，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/refresh.gif" alt="no-shadow" title="refresh"></div><p>上拉加载更多，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/loadMore.gif" alt="no-shadow" title="load more"></div><p>点赞，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/liked.gif" alt="no-shadow" title="liked"></div><p>缺省页(空数据)，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/empty.gif" alt="no-shadow" title="empty"></div><p>loading页，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/loading.gif" alt="no-shadow" title="loading"></div><p>渐变的Appbar，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/appbar.gif" alt="no-shadow" title="appbar"></div><p>评论，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/comment.gif" alt="no-shadow" title="comment"></div><p>我的页面，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/profile.gif" alt="no-shadow" title="profile"></div><p>以上只是 App 的一部分功能，大家也可以也可以点击观看 <a href="https://www.bilibili.com/video/BV1ur4y1A7of" target="_blank" rel="noopener">演示视频</a>，或者扫描二维码下载 App(android) 体验：</p><div style="width: 26%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/flutterCase1.0.2.png" alt="no-shadow" title="apk"></div><p>Tip:App 可以使用登录账户信息：任意一个作者名字，密码都是：666666，如：</p><table><thead><tr><th style="text-align:left">用户名</th><th style="text-align:left">密码</th></tr></thead><tbody><tr><td style="text-align:left">persilee</td><td style="text-align:left">密码：666666</td></tr><tr><td style="text-align:left">摄影师蝈蝈小姐</td><td style="text-align:left">密码：666666</td></tr><tr><td style="text-align:left">翠花小拍</td><td style="text-align:left">密码：666666</td></tr></tbody></table><hr><p>在介绍这套 <strong>MVVM</strong> 开发模式之前，我们首先需要了解 <code>riverpod</code> 和 <code>retrofit</code> 是什么。</p><p>下面我们来分别了解他们是什么。</p><h2 id="riverpod"><a href="#riverpod" class="headerlink" title="riverpod"></a>riverpod</h2><p><strong>riverpod</strong> 是 <strong>Flutter</strong> 状态管理库，flutter 的状态管理库有很多，例如： <code>Redux</code>、 <code>Bloc</code>、 <code>Provider</code> 等，flutter 官方推荐我们使用 <code>provider</code>，一般我们使用 <code>provider</code> 的时候，会结合 <code>ChangeNotifier</code> 、 <code>StateNotifier</code>、 <code>freezed</code> 去使用，而 <code>riverpod</code> 是 <code>provider</code> 的一个升级加强版，解决了 <code>provider</code> 一些疑难杂症，在这里就不过多介绍，如想了解更多 <code>riverpod</code> 信息，可以访问 <a href="https://riverpod.dev/" target="_blank" rel="noopener">riverpod官网</a> ，也可以参考我之前写的以下<a href="https://github.com/persilee/flutter_pro/tree/master/lib/demo/provider_demo" target="_blank" rel="noopener">Demo</a> 。</p><h2 id="retrofit"><a href="#retrofit" class="headerlink" title="retrofit"></a>retrofit</h2><p><strong>retrofit</strong> 是一个网络请求库，做过 android 的同学应该比较熟悉，可以用注解的方式生成请求 Rest Api 的各种方法，如，以下的简单的用法：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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">import</span> <span class="string">'package:retrofit/retrofit.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">part</span> <span class="string">'api_client.g.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@RestApi</span>(baseUrl: <span class="string">'https://api.lishaoy.net'</span>)</span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">ApiClient</span> </span>&#123;</span><br><span class="line">  <span class="keyword">factory</span> ApiClient(&#123;Dio dio, <span class="built_in">String</span> baseUrl&#125;) &#123;</span><br><span class="line">    dio ??= BaseDio.getInstance().getDio();</span><br><span class="line">    <span class="keyword">return</span> _ApiClient(dio, baseUrl: baseUrl);</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">   * 获取首页推荐文章</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="meta">@GET</span>(<span class="string">'/posts'</span>)</span><br><span class="line">  Future&lt;PostModel&gt; getPosts(</span><br><span class="line">      <span class="meta">@Query</span>(<span class="string">'pageIndex'</span>) <span class="built_in">String</span> pageIndex, <span class="meta">@Query</span>(<span class="string">'pageSize'</span>) <span class="built_in">String</span> pageSize,</span><br><span class="line">      &#123;<span class="meta">@Query</span>(<span class="string">'sort'</span>) <span class="built_in">String</span> sort = <span class="string">'recommend'</span>&#125;);</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="meta">@GET</span>(<span class="string">'/posts/&#123;postId&#125;'</span>)</span><br><span class="line">  Future&lt;SinglePostModel&gt; getPostsById(<span class="meta">@Path</span>(<span class="string">'postId'</span>) <span class="built_in">int</span> postId,</span><br><span class="line">      &#123;<span class="meta">@Query</span>(<span class="string">'notView'</span>) <span class="built_in">bool</span> notView&#125;);</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="meta">@POST</span>(<span class="string">'/login'</span>)</span><br><span class="line">  Future&lt;LoginModel&gt; login(<span class="meta">@Body</span>() Login login);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更多详情可以访问 <a href="https://pub.dev/packages/retrofit" target="_blank" rel="noopener">pub.dev retrofit</a> 。</p><h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><p>接下来我们来看看项目的目录结构，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── android  <span class="comment">## 原生android目录</span></span><br><span class="line">│   ├── app</span><br><span class="line">│   └── gradle</span><br><span class="line">├── assets  <span class="comment">## 资源文件目录</span></span><br><span class="line">│   ├── fonts</span><br><span class="line">│   ├── images</span><br><span class="line">│   └── json</span><br><span class="line">├── ios <span class="comment">## 原生iOS目录</span></span><br><span class="line">│   ├── Flutter</span><br><span class="line">│   ├── Frameworks</span><br><span class="line">│   ├── Pods</span><br><span class="line">│   ├── Runner</span><br><span class="line">│   ├── Runner.xcodeproj</span><br><span class="line">│   └── Runner.xcworkspace</span><br><span class="line">└── lib <span class="comment">## 项目文件目录</span></span><br><span class="line">    ├── http <span class="comment">##对网格请求相关的封装</span></span><br><span class="line">    │   ├── api_client.dart <span class="comment">## rest api 请求类</span></span><br><span class="line">    │   ├── api_client.g.dart <span class="comment">## retrofit 自动生成的类</span></span><br><span class="line">    │   ├── base_dio.dart <span class="comment">## 对dio封装类</span></span><br><span class="line">    │   ├── base_error.dart <span class="comment">## 服务端基本错误类型封装类</span></span><br><span class="line">    │   └── header_interceptor.dart  <span class="comment">##网络请求拦截器</span></span><br><span class="line">    ├── models <span class="comment">## json序列化的model类，相对于MVVM的 M 层</span></span><br><span class="line">    ├── pages <span class="comment">## 主要的UI页面目录，相对于MVVM的 V 层</span></span><br><span class="line">    ├── utils <span class="comment">## 一些工具类</span></span><br><span class="line">    │   ├── date_util.dart</span><br><span class="line">    │   ├── screen_util.dart</span><br><span class="line">    │   ├── status_bar_util.dart</span><br><span class="line">    │   ├── timeline_util.dart</span><br><span class="line">    │   └── widget_util.dart</span><br><span class="line">    ├── view_model <span class="comment">## 处理数据状态，业务逻辑，相对于 MVVM的 VM 层</span></span><br><span class="line">    │   ├── details_view_model.dart</span><br><span class="line">    │   ├── login_view_model.dart</span><br><span class="line">    │   ├── posts_view_model.dart</span><br><span class="line">    │   └── profile_view_model.dart</span><br><span class="line">    └── widgets <span class="comment">##公用或自定义组件</span></span><br><span class="line">        ├── cache_image.dart</span><br><span class="line">        ├── custom_circular_rect_angle.dart</span><br><span class="line">        ├── custom_indicator.dart</span><br><span class="line">        ├── custom_tabs.dart</span><br><span class="line">        ├── error_page.dart</span><br><span class="line">        ├── gradient_button.dart</span><br><span class="line">        ├── icon_animation_widget.dart</span><br><span class="line">        ├── iconfont.dart</span><br><span class="line">        ├── image_paper.dart</span><br><span class="line">        ├── over_scroll_behavior.dart</span><br><span class="line">        ├── page_state.dart</span><br><span class="line">        ├── per_flexible_space_bar.dart</span><br><span class="line">        ├── pic_swiper.dart</span><br><span class="line">        └── refresh.dart</span><br></pre></td></tr></table></figure><p>从目录结构可知， <code>models</code>、 <code>pages</code>、<code>view_model</code> 分别是 MVVM 开发模式的 M(数据层)、 V(视图层)、 VM(通过riverpod的StateNotifier将数据层和视图层绑定，state变化时数据层也跟着变化，当然这里也可以处理一些页面逻辑)，做过 android 的同学应该知道 android 的MVVM是使用 jetpack 组件库里的 DataBinding 和 LiveData 完成的，我这套开发模式灵感就是来源于此。</p><h2 id="网络请求模块"><a href="#网络请求模块" class="headerlink" title="网络请求模块"></a>网络请求模块</h2><p>首先，我们来对网络请求模块封装一把，让它能够通用易用。</p><p><strong>retrofit</strong> 是依赖网络请求库的，我们可以选择不同的库，例如：<code>http</code>、<code>Dio</code> 等。</p><p>在这里我们选择 <code>Dio</code> ，如下，是官方提供的案例代码：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestApi</span>(baseUrl: <span class="string">"https://5d42a6e2bc64f90014a56ca0.mockapi.io/api/v1/"</span>)</span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">RestClient</span> </span>&#123;</span><br><span class="line">  <span class="keyword">factory</span> RestClient(Dio dio, &#123;<span class="built_in">String</span> baseUrl&#125;) = _RestClient;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@GET</span>(<span class="string">"/tasks"</span>)</span><br><span class="line">  Future&lt;<span class="built_in">List</span>&lt;Task&gt;&gt; getTasks();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Dio的封装"><a href="#Dio的封装" class="headerlink" title="Dio的封装"></a>Dio的封装</h3><p>它需要传一个 Dio 的实例和一个可选的 baseUrl，我们需要对这里重新封装一下，使用者不用传递任何参数就可以使用，也可以选择使用不同的网络库和 baseUrl；所以，我们要封装一个 <code>baseDio</code> 单例类，如果用户没有传，我们就传递一个默认的 <code>baseDio</code> 类，代码大概如下所示：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestApi</span>(baseUrl: <span class="string">'https://api.lishaoy.net'</span>)</span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">ApiClient</span> </span>&#123;</span><br><span class="line">  <span class="keyword">factory</span> ApiClient(&#123;Dio dio, <span class="built_in">String</span> baseUrl&#125;) &#123;</span><br><span class="line">    dio ??= BaseDio.getInstance().getDio();</span><br><span class="line">    <span class="keyword">return</span> _ApiClient(dio, baseUrl: baseUrl);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@POST</span>(<span class="string">'/login'</span>)</span><br><span class="line">  Future&lt;LoginModel&gt; login(<span class="meta">@Body</span>() Login login);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>所以我要对 <code>Dio</code> 进行一次封装，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'package:dio/dio.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:pretty_dio_logger/pretty_dio_logger.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:pro_flutter/http/base_error.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:pro_flutter/http/header_interceptor.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BaseDio</span> </span>&#123;</span><br><span class="line">  BaseDio._(); <span class="comment">// 把构造方法私有化</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> BaseDio _instance; </span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> BaseDio getInstance() &#123;  <span class="comment">// 通过 getInstance 获取实例</span></span><br><span class="line">    _instance ??= BaseDio._();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> _instance;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  Dio getDio() &#123;</span><br><span class="line">    <span class="keyword">final</span> Dio dio = Dio();</span><br><span class="line">    dio.options = BaseOptions(receiveTimeout: <span class="number">66000</span>, connectTimeout: <span class="number">66000</span>); <span class="comment">// 设置超时时间等 ...</span></span><br><span class="line">    dio.interceptors.add(HeaderInterceptor()); <span class="comment">// 添加拦截器，如 token之类，需要全局使用的参数</span></span><br><span class="line">    dio.interceptors.add(PrettyDioLogger(  <span class="comment">// 添加日志格式化工具类</span></span><br><span class="line">      requestHeader: <span class="keyword">true</span>,</span><br><span class="line">      requestBody: <span class="keyword">true</span>,</span><br><span class="line">      responseBody: <span class="keyword">true</span>,</span><br><span class="line">      responseHeader: <span class="keyword">false</span>,</span><br><span class="line">      compact: <span class="keyword">false</span>,</span><br><span class="line">    ));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> dio;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  BaseError getDioError(<span class="built_in">Object</span> obj) &#123;  <span class="comment">// 这里封装了一个 BaseError 类，会根据后端返回的code返回不同的错误类</span></span><br><span class="line">    <span class="keyword">switch</span> (obj.runtimeType) &#123;</span><br><span class="line">      <span class="keyword">case</span> DioError:</span><br><span class="line">        <span class="keyword">if</span> ((obj <span class="keyword">as</span> DioError).type == DioErrorType.RESPONSE) &#123;</span><br><span class="line">          <span class="keyword">final</span> response = (obj <span class="keyword">as</span> DioError).response;</span><br><span class="line">          <span class="keyword">if</span> (response.statusCode == <span class="number">401</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> NeedLogin();</span><br><span class="line">          &#125; <span class="keyword">else</span> <span class="keyword">if</span> (response.statusCode == <span class="number">403</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> NeedAuth();</span><br><span class="line">          &#125; <span class="keyword">else</span> <span class="keyword">if</span> (response.statusCode == <span class="number">408</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> UserNotExist();</span><br><span class="line">          &#125; <span class="keyword">else</span> <span class="keyword">if</span> (response.statusCode == <span class="number">409</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> PwdNotMatch();</span><br><span class="line">          &#125; <span class="keyword">else</span> <span class="keyword">if</span> (response.statusCode == <span class="number">405</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> UserNameEmpty();</span><br><span class="line">          &#125; <span class="keyword">else</span> <span class="keyword">if</span> (response.statusCode == <span class="number">406</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> PwdEmpty();</span><br><span class="line">          &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> OtherError(</span><br><span class="line">              statusCode: response.statusCode,</span><br><span class="line">              statusMessage: response.statusMessage,</span><br><span class="line">            );</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> OtherError();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="BaseError的封装"><a href="#BaseError的封装" class="headerlink" title="BaseError的封装"></a>BaseError的封装</h3><p>以上代码中的 <code>BaseError</code> 类是一个抽象类，我们可以实现这个抽象类，告诉UI不同的错误类型，UI只需要用实现类就可以访问错误码和错误消息，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">int</span> code;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">String</span> message;</span><br><span class="line"></span><br><span class="line">  BaseError(&#123;<span class="keyword">this</span>.code, <span class="keyword">this</span>.message&#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">NeedLogin</span> <span class="keyword">implements</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> code =&gt; <span class="number">401</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> <span class="keyword">get</span> message =&gt; <span class="string">"请先登录"</span>;</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">NeedAuth</span> <span class="keyword">implements</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> code =&gt; <span class="number">403</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> <span class="keyword">get</span> message =&gt; <span class="string">"非法访问，请使用正确的token"</span>;</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">UserNotExist</span> <span class="keyword">implements</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> code =&gt; <span class="number">408</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> <span class="keyword">get</span> message =&gt; <span class="string">"用户不存在"</span>;</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">UserNameEmpty</span> <span class="keyword">implements</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> code =&gt; <span class="number">405</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> <span class="keyword">get</span> message =&gt; <span class="string">"用户名不能为空"</span>;</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">PwdNotMatch</span> <span class="keyword">implements</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> code =&gt; <span class="number">409</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> <span class="keyword">get</span> message =&gt; <span class="string">"用户密码不正确"</span>;</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">PwdEmpty</span> <span class="keyword">implements</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> code =&gt; <span class="number">406</span>;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> <span class="keyword">get</span> message =&gt; <span class="string">"用户密码不能为空"</span>;</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">OtherError</span> <span class="keyword">implements</span> <span class="title">BaseError</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">int</span> statusCode;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">String</span> statusMessage;</span><br><span class="line"></span><br><span class="line">  OtherError(&#123;<span class="keyword">this</span>.statusCode, <span class="keyword">this</span>.statusMessage&#125;);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">int</span> <span class="keyword">get</span> code =&gt; statusCode;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> <span class="keyword">get</span> message =&gt; statusMessage;</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><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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">@RestApi</span>(baseUrl: <span class="string">'https://api.lishaoy.net'</span>)</span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">ApiClient</span> </span>&#123;</span><br><span class="line">  <span class="keyword">factory</span> ApiClient(&#123;Dio dio, <span class="built_in">String</span> baseUrl&#125;) &#123;</span><br><span class="line">    dio ??= BaseDio.getInstance().getDio();</span><br><span class="line">    <span class="keyword">return</span> _ApiClient(dio, baseUrl: baseUrl);</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">   * 获取首页推荐文章</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="meta">@GET</span>(<span class="string">'/posts'</span>)</span><br><span class="line">  Future&lt;PostModel&gt; getPosts(</span><br><span class="line">      <span class="meta">@Query</span>(<span class="string">'pageIndex'</span>) <span class="built_in">String</span> pageIndex, <span class="meta">@Query</span>(<span class="string">'pageSize'</span>) <span class="built_in">String</span> pageSize,</span><br><span class="line">      &#123;<span class="meta">@Query</span>(<span class="string">'sort'</span>) <span class="built_in">String</span> sort = <span class="string">'recommend'</span>&#125;);</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="meta">@GET</span>(<span class="string">'/posts/&#123;postId&#125;'</span>)</span><br><span class="line">  Future&lt;SinglePostModel&gt; getPostsById(<span class="meta">@Path</span>(<span class="string">'postId'</span>) <span class="built_in">int</span> postId,</span><br><span class="line">      &#123;<span class="meta">@Query</span>(<span class="string">'notView'</span>) <span class="built_in">bool</span> notView&#125;);</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="meta">@POST</span>(<span class="string">'/login'</span>)</span><br><span class="line">  Future&lt;LoginModel&gt; login(<span class="meta">@Body</span>() Login login);</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="meta">@POST</span>(<span class="string">'/posts/&#123;postId&#125;/like'</span>)</span><br><span class="line">  Future&lt;BaseModel&gt; like(<span class="meta">@Path</span>(<span class="string">'postId'</span>) <span class="built_in">int</span> postId);</span><br><span class="line"></span><br><span class="line">  ...</span><br></pre></td></tr></table></figure><p>然后，我们会在 view model 使用它，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 点赞</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Future&lt;<span class="keyword">void</span>&gt; clickLike(<span class="built_in">int</span> postId, <span class="built_in">int</span> index) <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    BaseModel data = <span class="keyword">await</span> ApiClient().like(postId); <span class="comment">// 使用非常简单一句代码即可</span></span><br><span class="line">    <span class="keyword">if</span> (data.message == <span class="string">'success'</span>) &#123;</span><br><span class="line">      updatePostById(postId, index);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    state = state.copyWith(</span><br><span class="line">        pageState: PageState.errorState,</span><br><span class="line">        error: BaseDio.getInstance().getDioError(e));</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="View-Model-模块"><a href="#View-Model-模块" class="headerlink" title="View Model 模块"></a>View Model 模块</h2><p>View Model 模块主要处理数据和状态的绑定、业务逻辑等。</p><h3 id="创建状态类"><a href="#创建状态类" class="headerlink" title="创建状态类"></a>创建状态类</h3><p>我们首先需要创建一个状态类，来存放数据状态和页面状态等，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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">/// 存储页面状态和数据状态（如，缺省页、错误页、加载中...）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostState</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">List</span>&lt;Post&gt; posts;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">List</span>&lt;Category&gt; categories;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">int</span> pageIndex;</span><br><span class="line">  <span class="keyword">final</span> PageState pageState; <span class="comment">// 页面状态类</span></span><br><span class="line">  <span class="keyword">final</span> BaseError error; <span class="comment">// 根据后端返回的错误的错误类</span></span><br><span class="line"></span><br><span class="line">  PostState(</span><br><span class="line">      &#123;<span class="keyword">this</span>.posts,</span><br><span class="line">      <span class="keyword">this</span>.categories,</span><br><span class="line">      <span class="keyword">this</span>.pageIndex,</span><br><span class="line">      <span class="keyword">this</span>.pageState,</span><br><span class="line">      <span class="keyword">this</span>.error&#125;);</span><br><span class="line"></span><br><span class="line">  PostState.initial()</span><br><span class="line">      : posts = [],</span><br><span class="line">        categories = [],</span><br><span class="line">        pageIndex = <span class="number">1</span>,</span><br><span class="line">        pageState = PageState.initializedState,</span><br><span class="line">        error = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">  PostState copyWith(&#123;</span><br><span class="line">    <span class="built_in">List</span>&lt;Post&gt; posts,</span><br><span class="line">    <span class="built_in">List</span>&lt;Category&gt; categories,</span><br><span class="line">    <span class="built_in">int</span> pageIndex,</span><br><span class="line">    PageState pageState,</span><br><span class="line">    BaseError error,</span><br><span class="line">  &#125;) &#123;</span><br><span class="line">    <span class="keyword">return</span> PostState(</span><br><span class="line">      posts: posts ?? <span class="keyword">this</span>.posts,</span><br><span class="line">      categories: categories ?? <span class="keyword">this</span>.categories,</span><br><span class="line">      pageIndex: pageIndex ?? <span class="keyword">this</span>.pageIndex,</span><br><span class="line">      pageState: pageState ?? <span class="keyword">this</span>.pageState,</span><br><span class="line">      error: error ?? <span class="keyword">this</span>.error,</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>当然这个状态类也可以用 <code>freezed</code> 自动生成。</p><h3 id="请求网络数据和处理页面状态"><a href="#请求网络数据和处理页面状态" class="headerlink" title="请求网络数据和处理页面状态"></a>请求网络数据和处理页面状态</h3><p>我们会返回这个状态类给UI，riverpod 的 StateNotifier 会监听这个状态类里的所有成员变量，当我们更改这些数据之后，UI会自动刷新，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span 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">  Future&lt;<span class="keyword">void</span>&gt; getPosts(<span class="built_in">int</span> categoryId, &#123;<span class="built_in">bool</span> isRefresh = <span class="keyword">false</span>&#125;) <span class="keyword">async</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (state.pageState == PageState.initializedState) &#123;</span><br><span class="line">      state = state.copyWith(pageState: PageState.busyState); <span class="comment">// UI收到这个状态可以呈现loading页面</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (isRefresh) &#123;  <span class="comment">// 下拉刷新</span></span><br><span class="line">        PostModel postModel;</span><br><span class="line">        <span class="keyword">if</span>(categoryId == <span class="number">-2</span>) &#123;</span><br><span class="line">          state = state.copyWith(pageState: PageState.emptyDataState); <span class="comment">// UI收到这个状态，可以显示缺省页空数据</span></span><br><span class="line">          <span class="keyword">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (categoryId == <span class="number">-1</span>) &#123;</span><br><span class="line">          postModel = <span class="keyword">await</span> ApiClient().getPosts(<span class="string">'1'</span>, <span class="string">'10'</span>); <span class="comment">// 请求网络接口</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          postModel =</span><br><span class="line">              <span class="keyword">await</span> ApiClient().getPostsByCategoryId(<span class="string">'1'</span>, <span class="string">'10'</span>, categoryId);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (postModel.data.posts.isEmpty &amp;&amp; state.pageIndex == <span class="number">1</span>) &#123;</span><br><span class="line">          state = state.copyWith(pageState: PageState.emptyDataState);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          initPostState();</span><br><span class="line">          state = state.copyWith(</span><br><span class="line">            posts: [...postModel.data.posts],  <span class="comment">// 把数据发给UI</span></span><br><span class="line">            pageState: PageState.refreshState, <span class="comment">// 更改页面状态为刷新</span></span><br><span class="line">            pageIndex: <span class="number">2</span>,</span><br><span class="line">          );</span><br><span class="line">        &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 下拉加载更多</span></span><br><span class="line">        PostModel postModel;</span><br><span class="line">        <span class="keyword">if</span>(categoryId == <span class="number">-2</span>) &#123;</span><br><span class="line">          state = state.copyWith(pageState: PageState.emptyDataState); <span class="comment">// UI收到这个状态可以呈现loading页面</span></span><br><span class="line">          <span class="keyword">return</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (categoryId == <span class="number">-1</span>) &#123;</span><br><span class="line">          postModel =</span><br><span class="line">              <span class="keyword">await</span> ApiClient().getPosts(state.pageIndex.toString(), <span class="string">'10'</span>); <span class="comment">// 请求网络接口</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          postModel = <span class="keyword">await</span> ApiClient().getPostsByCategoryId(</span><br><span class="line">              state.pageIndex.toString(), <span class="string">'10'</span>, categoryId);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (postModel.data.posts.isEmpty &amp;&amp; state.pageIndex == <span class="number">1</span>) &#123;</span><br><span class="line">          state = state.copyWith(pageState: PageState.emptyDataState);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          state = state.copyWith(</span><br><span class="line">              posts: [...state.posts, ...postModel.data.posts],  <span class="comment">// 把数据发给UI</span></span><br><span class="line">              pageIndex: state.pageIndex + <span class="number">1</span>,</span><br><span class="line">              pageState: PageState.dataFetchState); <span class="comment">// 更改页面状态</span></span><br><span class="line">          <span class="keyword">if</span> (postModel.data.posts.isEmpty ||</span><br><span class="line">              postModel.data.posts.length &lt; <span class="number">10</span>) &#123;</span><br><span class="line">            state = state.copyWith(pageState: PageState.noMoreDataState);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">      state = state.copyWith(</span><br><span class="line">          pageState: PageState.errorState,  <span class="comment">// 如果发生错误，更改页面状态</span></span><br><span class="line">          error: BaseDio.getInstance().getDioError(e));</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>以上一个方面就完成了应用首页的所有列表数据请求和页面状态处理，在UI层，不需要写 setState() 和 请求数据的任何代码，UI层只是呈现UI。</p><h2 id="View-模块"><a href="#View-模块" class="headerlink" title="View 模块"></a>View 模块</h2><p>那么在UI层怎么处理这些状态呢？</p><p>这也非常简单，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="comment">// 创建provider，返回viewModel</span></span><br><span class="line"><span class="keyword">final</span> postsProvider = StateNotifierProvider.family&lt;PostsViewModel, <span class="built_in">int</span>&gt;(</span><br><span class="line">    (ref, categoryId) =&gt; PostsViewModel(categoryId));</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostsPageCategory</span> <span class="keyword">extends</span> <span class="title">ConsumerWidget</span> </span>&#123;  <span class="comment">// 继承 ConsumerWidget</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">int</span> categoryId;</span><br><span class="line">  <span class="keyword">final</span> ScrollController scrollController;</span><br><span class="line">  <span class="keyword">final</span> RefreshController refreshController;</span><br><span class="line"></span><br><span class="line">  PostsPageCategory(</span><br><span class="line">      &#123;<span class="keyword">this</span>.categoryId, <span class="keyword">this</span>.scrollController, <span class="keyword">this</span>.refreshController&#125;);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context, ScopedReader watch) &#123; </span><br><span class="line">    <span class="keyword">final</span> postsViewModel = watch(postsProvider(categoryId)); <span class="comment">// 使用 watch 来监听Provider</span></span><br><span class="line">    <span class="keyword">final</span> postState = watch(postsProvider(categoryId).state); <span class="comment">// 使用 watch 来监听Provider的状态</span></span><br><span class="line">    <span class="keyword">return</span> Refresh(</span><br><span class="line">      controller: refreshController,</span><br><span class="line">      onLoading: () <span class="keyword">async</span> &#123;  <span class="comment">// 加载更多处理</span></span><br><span class="line">        <span class="keyword">await</span> postsViewModel.getPosts(categoryId);</span><br><span class="line">        <span class="keyword">if</span> (postState.pageState == PageState.noMoreDataState) &#123;</span><br><span class="line">          refreshController.loadNoData();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          refreshController.loadComplete();</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      onRefresh: () <span class="keyword">async</span> &#123; <span class="comment">// 刷新处理</span></span><br><span class="line">        <span class="keyword">await</span> context</span><br><span class="line">            .read(postsProvider(categoryId))</span><br><span class="line">            .getPosts(categoryId, isRefresh: <span class="keyword">true</span>);</span><br><span class="line">        refreshController.refreshCompleted();</span><br><span class="line">        refreshController.footerMode.value = LoadStatus.canLoading;</span><br><span class="line">      &#125;,</span><br><span class="line">      content: _createContent(postState, context),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  Widget _createContent(PostState postState, BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">if</span> (postState.pageState == PageState.busyState ||</span><br><span class="line">        postState.pageState == PageState.initializedState) &#123;  <span class="comment">// loading 状态处理</span></span><br><span class="line">      <span class="keyword">return</span> Center(</span><br><span class="line">        child: Lottie.asset(</span><br><span class="line">          <span class="string">'assets/json/loading2.json'</span>,</span><br><span class="line">          width: <span class="number">126</span>,</span><br><span class="line">          fit: BoxFit.cover,</span><br><span class="line">          alignment: Alignment.center,</span><br><span class="line">        ),</span><br><span class="line">      );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (postState.pageState == PageState.emptyDataState) &#123;</span><br><span class="line">      <span class="keyword">return</span> ErrorPage( <span class="comment">// 错误处理</span></span><br><span class="line">        isEmptyPage: <span class="keyword">true</span>,</span><br><span class="line">        icon: Lottie.asset(</span><br><span class="line">          <span class="string">'assets/json/empty3.json'</span>,</span><br><span class="line">          width: ScreenUtil.instance.width / <span class="number">1.8</span>,</span><br><span class="line">          height: <span class="number">220</span>,</span><br><span class="line">          fit: BoxFit.contain,</span><br><span class="line">          alignment: Alignment.center,</span><br><span class="line">        ),</span><br><span class="line">        desc: <span class="string">'暂 无 数 据'</span>,</span><br><span class="line">        buttonAction: () =&gt; context.refresh(postsProvider(categoryId)),</span><br><span class="line">      );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (postState.pageState == PageState.errorState) &#123;</span><br><span class="line">      <span class="keyword">return</span> ErrorPage(</span><br><span class="line">        title: postState.error <span class="keyword">is</span> NeedLogin</span><br><span class="line">            ? <span class="string">'😮 你竟然忘记登录 😮'</span></span><br><span class="line">            : postState.error.code?.toString(),</span><br><span class="line">        desc: postState.error.message,</span><br><span class="line">        buttonAction: () <span class="keyword">async</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (postState.error <span class="keyword">is</span> NeedLogin) &#123;</span><br><span class="line">            LoginState loginState = <span class="keyword">await</span> Navigator.of(context).push(</span><br><span class="line">                MaterialPageRoute(builder: (context) =&gt; FlareSignInDemo()));</span><br><span class="line">            <span class="keyword">if</span> (loginState.isLogin) &#123;</span><br><span class="line">              context.refresh(postsProvider(categoryId));</span><br><span class="line">            &#125;</span><br><span class="line">          &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            context.refresh(postsProvider(categoryId));</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        buttonText: postState.error <span class="keyword">is</span> NeedLogin ? <span class="string">'登录'</span> : <span class="keyword">null</span>,</span><br><span class="line">      );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ListView.separated(  <span class="comment">// 加载数据，现在页面</span></span><br><span class="line">      shrinkWrap: <span class="keyword">true</span>,</span><br><span class="line">      separatorBuilder: (context, index) &#123;</span><br><span class="line">        <span class="keyword">return</span> Padding(padding: EdgeInsets.only(top: <span class="number">12</span>));</span><br><span class="line">      &#125;,</span><br><span class="line">      padding: EdgeInsets.fromLTRB(<span class="number">12</span>, <span class="number">18</span>, <span class="number">12</span>, <span class="number">18</span>),</span><br><span class="line">      reverse: <span class="keyword">false</span>,</span><br><span class="line">      itemCount: postState.posts.length,</span><br><span class="line">      controller: scrollController,</span><br><span class="line">      itemBuilder: (BuildContext context, <span class="built_in">int</span> index) &#123;</span><br><span class="line">        <span class="keyword">return</span> PostsPageItem(</span><br><span class="line">          post: postState.posts[index],</span><br><span class="line">          index: index,</span><br><span class="line">          categoryId: categoryId,</span><br><span class="line">        );</span><br><span class="line">      &#125;,</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>是不是非常简单，不需要写 setState() 和 请求数据的任何代码，代码结构也非常清晰。在上述APP应用里的首页以及分类页面列表数据及页面的loading和缺省页等都是这一个简单 <code>PostsPageCategory</code> 完成的。</p><h2 id="其他相关"><a href="#其他相关" class="headerlink" title="其他相关"></a>其他相关</h2><p>以上这套开发模式我给出了大概的思路和部分代码，大家也可以顺着这个思路试试；这套开发模式后续还会继续优化它。</p><h3 id="应用功能相关"><a href="#应用功能相关" class="headerlink" title="应用功能相关"></a>应用功能相关</h3><p>用过 Flutter TabBar 同学应该知道，它在字体放大时会卡顿，以及如何自定义指示器等， 如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/TabBar.gif" alt="no-shadow" title="TabBar"></div><p>以及，渐变的高斯模糊背景和图片标题动画的实现等，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/image/flutterMVVM/profile.gif" alt="no-shadow" title="profile"></div><p>及更多这个应用的功能实现和细节并没有在这里讲述，这篇文章主要介绍 MVVM，关于这个图片分享APP，只是我在业余时间对Flutter的研究探索和学习，这个应用大概只完成了一半，后续应该还好写关于这个APP的文章。</p><h3 id="REST-API接口相关"><a href="#REST-API接口相关" class="headerlink" title="REST API接口相关"></a>REST API接口相关</h3><p>还有，这个APP的后端API也是我自己开发的，使用的是 nodejs 的 <strong>express</strong> + <strong>ts</strong> 开发的，如首页推荐接口及分类页接口数据都是通过这个API查询到的： <a href="https://api.lishaoy.net/posts?sort=recommend&amp;pageIndex=1&amp;pageSize=10" target="_blank" rel="noopener">首页API接口</a></p><p>具体的实现是使用一条SQL语句查询得到，代码如下：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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></pre></td><td class="code"><pre><span class="line">  <span class="keyword">SELECT</span> </span><br><span class="line">  post.id, </span><br><span class="line">  post.content, </span><br><span class="line">  post.title,</span><br><span class="line">  category.name <span class="keyword">as</span> <span class="keyword">category</span>,</span><br><span class="line">  post.views,</span><br><span class="line">  JSON_OBJECT(</span><br><span class="line">    <span class="string">'id'</span>, user.id,</span><br><span class="line">    <span class="string">'name'</span>, user.name,</span><br><span class="line">    <span class="string">'avatar'</span>, <span class="keyword">CAST</span>(</span><br><span class="line">      <span class="keyword">IF</span>(<span class="keyword">COUNT</span>(avatar.id), </span><br><span class="line">        <span class="keyword">GROUP_CONCAT</span>(</span><br><span class="line">          <span class="keyword">DISTINCT</span> JSON_OBJECT(</span><br><span class="line">            <span class="string">'largeAvatarUrl'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/avatar/'</span>, user.id, <span class="string">'|@u003f|size=large'</span>),</span><br><span class="line">            <span class="string">'mediumAvatarUrl'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/avatar/'</span>, user.id, <span class="string">'|@u003f|size=medium'</span>),</span><br><span class="line">            <span class="string">'smallAvatarUrl'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/avatar/'</span>, user.id, <span class="string">'|@u003f|size=small'</span>)</span><br><span class="line">          )</span><br><span class="line">        ),</span><br><span class="line">      <span class="literal">NULL</span>)</span><br><span class="line">    <span class="keyword">AS</span> <span class="keyword">JSON</span>)</span><br><span class="line">  ) <span class="keyword">as</span> <span class="keyword">user</span>,</span><br><span class="line">  (</span><br><span class="line">    <span class="keyword">SELECT</span> <span class="keyword">COUNT</span>(comment.id) <span class="keyword">FROM</span> <span class="keyword">comment</span></span><br><span class="line">    <span class="keyword">WHERE</span> comment.postId = post.id</span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> comment.postId</span><br><span class="line"> ) <span class="keyword">as</span> totalComments,   </span><br><span class="line">  <span class="keyword">CAST</span>(</span><br><span class="line">    <span class="keyword">IF</span>(</span><br><span class="line">      <span class="keyword">COUNT</span>(cover.id),</span><br><span class="line">          <span class="keyword">GROUP_CONCAT</span>(</span><br><span class="line">            <span class="keyword">DISTINCT</span> JSON_OBJECT(</span><br><span class="line">              <span class="string">'id'</span>, cover.id,</span><br><span class="line">              <span class="string">'width'</span>, cover.width,</span><br><span class="line">              <span class="string">'height'</span>, cover.height,</span><br><span class="line">              <span class="string">'largeImageUrl'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/files/'</span>, cover.id, <span class="string">'/serve|@u003f|size=large'</span>),</span><br><span class="line">              <span class="string">'mediumImageUrl'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/files/'</span>, cover.id, <span class="string">'/serve|@u003f|size=medium'</span>),</span><br><span class="line">              <span class="string">'small'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/files/'</span>, cover.id, <span class="string">'/serve|@u003f|size=thumbnail'</span>)</span><br><span class="line">            ) <span class="keyword">ORDER</span> <span class="keyword">BY</span> cover.id <span class="keyword">DESC</span></span><br><span class="line">          ),</span><br><span class="line">      <span class="literal">NULL</span></span><br><span class="line">    ) <span class="keyword">AS</span> <span class="keyword">JSON</span></span><br><span class="line">  ) <span class="keyword">AS</span> coverImage,</span><br><span class="line">  <span class="keyword">CAST</span>(</span><br><span class="line">    <span class="keyword">IF</span>(</span><br><span class="line">      <span class="keyword">COUNT</span>(file.id),</span><br><span class="line">      <span class="keyword">CONCAT</span>(</span><br><span class="line">        <span class="string">'['</span>,</span><br><span class="line">          <span class="keyword">GROUP_CONCAT</span>(</span><br><span class="line">            <span class="keyword">DISTINCT</span> JSON_OBJECT(</span><br><span class="line">              <span class="string">'id'</span>, file.id,</span><br><span class="line">              <span class="string">'width'</span>, file.width,</span><br><span class="line">              <span class="string">'height'</span>, file.height,</span><br><span class="line">              <span class="string">'largeImageUrl'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/files/'</span>, file.id, <span class="string">'/serve|@u003f|size=large'</span>),</span><br><span class="line">              <span class="string">'mediumImageUrl'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/files/'</span>, file.id, <span class="string">'/serve|@u003f|size=medium'</span>),</span><br><span class="line">              <span class="string">'small'</span>, <span class="keyword">concat</span>(<span class="string">'http://localhost:3001/files/'</span>, file.id, <span class="string">'/serve|@u003f|size=thumbnail'</span>)</span><br><span class="line">            ) <span class="keyword">ORDER</span> <span class="keyword">BY</span> file.id <span class="keyword">DESC</span></span><br><span class="line">          ),</span><br><span class="line">        <span class="string">']'</span></span><br><span class="line">      ),</span><br><span class="line">      <span class="literal">NULL</span></span><br><span class="line">    ) <span class="keyword">AS</span> <span class="keyword">JSON</span></span><br><span class="line">  ) <span class="keyword">AS</span> files,</span><br><span class="line">  <span class="keyword">CAST</span>(</span><br><span class="line">    <span class="keyword">IF</span>(</span><br><span class="line">      <span class="keyword">COUNT</span>(tag.id),</span><br><span class="line">      <span class="keyword">CONCAT</span>(</span><br><span class="line">        <span class="string">'['</span>, </span><br><span class="line">          <span class="keyword">GROUP_CONCAT</span>(</span><br><span class="line">            <span class="keyword">DISTINCT</span> JSON_OBJECT(</span><br><span class="line">              <span class="string">'id'</span>, tag.id,</span><br><span class="line">              <span class="string">'name'</span>, tag.name</span><br><span class="line">            )</span><br><span class="line">          ),</span><br><span class="line">        <span class="string">']'</span></span><br><span class="line">      ),</span><br><span class="line">      <span class="literal">NULL</span></span><br><span class="line">    ) <span class="keyword">AS</span> <span class="keyword">JSON</span></span><br><span class="line">  ) <span class="keyword">AS</span> tags,</span><br><span class="line">  (</span><br><span class="line">    <span class="keyword">SELECT</span> <span class="keyword">COUNT</span>(user_like_post.postId)</span><br><span class="line">    <span class="keyword">FROM</span> user_like_post</span><br><span class="line">    <span class="keyword">WHERE</span> user_like_post.postId = post.id</span><br><span class="line">  ) <span class="keyword">AS</span> totalLikes</span><br><span class="line"><span class="keyword">FROM</span> post </span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> <span class="keyword">user</span> </span><br><span class="line">    <span class="keyword">ON</span> user.id = post.userId</span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> avatar</span><br><span class="line">    <span class="keyword">ON</span> avatar.userId = user.id</span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> LATERAL (</span><br><span class="line">    <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">file</span></span><br><span class="line">    <span class="keyword">WHERE</span> file.postId = post.id</span><br><span class="line">    <span class="keyword">ORDER</span> <span class="keyword">BY</span> file.id <span class="keyword">DESC</span></span><br><span class="line">    <span class="keyword">LIMIT</span> <span class="number">9</span></span><br><span class="line">  ) <span class="keyword">AS</span> <span class="keyword">file</span> <span class="keyword">ON</span> file.postId = post.id</span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> LATERAL(</span><br><span class="line"> <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">file</span></span><br><span class="line"> <span class="keyword">WHERE</span> file.isCover = <span class="number">1</span> <span class="keyword">AND</span> file.postId = post.id</span><br><span class="line"> <span class="keyword">GROUP</span> <span class="keyword">BY</span> file.id</span><br><span class="line"> <span class="keyword">LIMIT</span> <span class="number">1</span></span><br><span class="line">  ) <span class="keyword">AS</span> cover <span class="keyword">ON</span> cover.postId = post.id <span class="keyword">and</span> cover.isCover = <span class="number">1</span> </span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> post_tag</span><br><span class="line">  <span class="keyword">ON</span> post_tag.postId = post.id</span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> tag</span><br><span class="line">  <span class="keyword">ON</span> tag.id = post_tag.tagId</span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> <span class="keyword">category</span> </span><br><span class="line">  <span class="keyword">ON</span> post.categoryId = category.id</span><br><span class="line"><span class="keyword">WHERE</span> post.id <span class="keyword">IS</span> <span class="keyword">NOT</span> <span class="literal">NULL</span></span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> post.id</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> post.id <span class="keyword">DESC</span></span><br><span class="line"><span class="keyword">LIMIT</span> <span class="number">10</span></span><br><span class="line"><span class="keyword">OFFSET</span> <span class="number">0</span></span><br></pre></td></tr></table></figure><p>这个是打印出来的log，具体的代码如下(可根据不同的参数查询不同的数据)，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="keyword">export</span> <span class="keyword">const</span> getPosts = <span class="keyword">async</span> (options: GetPostOptions) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123;</span><br><span class="line">    sort,</span><br><span class="line">    filter,</span><br><span class="line">    pagination: &#123; limit, offset &#125;,</span><br><span class="line">    userId,</span><br><span class="line">  &#125; = options;</span><br><span class="line">  <span class="keyword">let</span> params: <span class="built_in">Array</span>&lt;any&gt; = [limit, offset];</span><br><span class="line">  <span class="keyword">if</span> (filter.param) &#123;</span><br><span class="line">    params = [filter.param, ...params];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (userId) &#123;</span><br><span class="line">    params = [userId, ...params];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`params: <span class="subst">$&#123;params&#125;</span>`</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> sql = <span class="string">`</span></span><br><span class="line"><span class="string">  SELECT </span></span><br><span class="line"><span class="string">    post.id, </span></span><br><span class="line"><span class="string">    post.content, </span></span><br><span class="line"><span class="string">    post.title,</span></span><br><span class="line"><span class="string">    category.name as category,</span></span><br><span class="line"><span class="string">    post.views,</span></span><br><span class="line"><span class="string">    post.createdAt,</span></span><br><span class="line"><span class="string">    post.updatedAt,</span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.user&#125;</span>,</span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.totalComments&#125;</span>,</span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.coverImage&#125;</span>,</span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.file&#125;</span>,</span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.tags&#125;</span></span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;userId ? <span class="string">`, <span class="subst">$&#123;sqlFragment.liked&#125;</span> `</span> : <span class="string">''</span>&#125;</span>,</span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.totalLikes&#125;</span></span></span><br><span class="line"><span class="string">  FROM post </span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.leftJoinUser&#125;</span></span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.leftJoinOneFile&#125;</span></span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.leftJoinCover&#125;</span></span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.leftJoinTag&#125;</span></span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;sqlFragment.leftJoinCategory&#125;</span></span></span><br><span class="line"><span class="string">    <span class="subst">$&#123;filter.name == <span class="string">'userLiked'</span> ? sqlFragment.innerJoinUserLikePost : <span class="string">''</span>&#125;</span></span></span><br><span class="line"><span class="string">  WHERE <span class="subst">$&#123;filter.sql&#125;</span></span></span><br><span class="line"><span class="string">  GROUP BY post.id</span></span><br><span class="line"><span class="string">  ORDER BY <span class="subst">$&#123;sort&#125;</span></span></span><br><span class="line"><span class="string">  LIMIT ?</span></span><br><span class="line"><span class="string">  OFFSET ?</span></span><br><span class="line"><span class="string">  `</span>;</span><br><span class="line"></span><br><span class="line">  <span class="built_in">console</span>.log(sql);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> [data] = <span class="keyword">await</span> connection.promise().query(sql, params);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> data;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>如果这个后端 REST API 接口应用感兴趣的同学可以参考 <a href="https://ninghao.net/package/xb2-node" target="_blank" rel="noopener">宁皓网</a> 的视频，我就是根据这套视频做的，不过自己加了很多东西。</p><p>最后，很多同学都希望我开源，所以，给出项目的地址，不过项目还没有完成，架构也在优化中…后续可能项目地址会变化，目前可以先参考以下地址：</p><p>项目地址：<a href="https://github.com/persilee/flutter_pro" target="_blank" rel="noopener">https://github.com/persilee/flutter_pro</a></p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/flutterMVVM.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Android响应式编程之RxJava</title>
      <link>https://h.lishaoy.net/rxjava.html</link>
      <guid>https://h.lishaoy.net/rxjava.html</guid>
      <pubDate>Fri, 07 Aug 2020 07:19:44 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/rxjava/rxjava_cover.png&quot; class=&quot;full-image&quot; alt=&quot;RxJava&quot; title=&quot;RxJava&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;section id=&quot;nice&quot; data-tool=&quot;mdnice编辑器&quot; data-website=&quot;https://www.mdnice.com&quot; style=&quot;font-size: 16px; padding: 0 10px; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; line-height: 1.35; color: #333; font-family: Optima-Regular, PingFangTC-light; letter-spacing: 1.5px;&quot;&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;&quot;&gt;本篇文章将概述 &lt;strong style=&quot;color: #399003; font-weight: bold;&quot;&gt;&lt;span&gt;「&lt;/span&gt;Android&lt;span&gt;」&lt;/span&gt;&lt;/strong&gt; 响应式编程 &lt;strong style=&quot;color: #399003; font-weight: bold;&quot;&gt;&lt;span&gt;「&lt;/span&gt;RxJava&lt;span&gt;」&lt;/span&gt;&lt;/strong&gt;，会从设计模式、使用到原理结合案例，由浅到深、由表到里、循序渐进的概述。&lt;/p&gt;&lt;/section&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/rxjava/rxjava_cover.png" class="full-image" alt="RxJava" title="RxJava"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; padding: 0 10px; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; line-height: 1.35; color: #333; font-family: Optima-Regular, PingFangTC-light; letter-spacing: 1.5px;"><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">本篇文章将概述 <strong style="color: #399003; font-weight: bold;"><span>「</span>Android<span>」</span></strong> 响应式编程 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong>，会从设计模式、使用到原理结合案例，由浅到深、由表到里、循序渐进的概述。</p></section><a id="more"></a><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; padding: 0 10px; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; line-height: 1.35; color: #333; font-family: Optima-Regular, PingFangTC-light; letter-spacing: 1.5px;"><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">本篇文章的示例代码放在 <a href="https://github.com/persilee/android_practice" style="text-decoration: none; word-wrap: break-word; color: #399003; font-weight: normal; border-bottom: 1px solid #399003;" target="_blank" rel="noopener">Github</a> 上。</p><h2 id="观察者模式(Observer pattern)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #4CAF50;"><span class="prefix" style="display: none;"></span><span class="content" style="display: flex; color: #4CAF50; font-size: 20px;">观察者模式(Observer pattern)</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 20px; height: 10px; border-top-left-radius: 20px; border-top-right-radius: 20px; background: RGBA(76, 175, 80, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">在使用 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 之前，我们需要先理解观察者设计模式，因为，<strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 就使用了观察者设计模式。</p><h3 id="定义" data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/grass-green.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>定义</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">观察者模式(又被称为发布-订阅 Publish/Subscribe 模式)，属于行为型模式的一种，它定义了一对多的依赖关系，让多个观察者对象同时监听某一个被观察者对象，被观察者对象在状态变化时，会通知所有的观察者对象，使他们能够自动更新。</p><h3 id="结构" data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/grass-green.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>结构</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">观察者模式结构，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/UML.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">一般我们写一个观察者模式都需要定义如下角色：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; font-size: 15px; color: #595959; list-style-type: circle;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 16px; font-weight: normal; color: #595959;">Observable(被观察者)：一般为抽象类，用于保存观察者对象和新增、删除、通知观察者的方法。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 16px; font-weight: normal; color: #595959;">ConcreteObservable(具体的被观察者)：继承 Observable 类，实现 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">notifyObservers()</code> 方法，当被观察者发生变化时，通知所有的观察者。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 16px; font-weight: normal; color: #595959;">Observer(观察者)：一般为接口，包含 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">update()</code> 方法，当收到具体的被观察者通知时被调用。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 16px; font-weight: normal; color: #595959;">ConcreteObserver(具体的观察者)：实现 Observer 接口，重写 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">update()</code> 方法，以便更新自身状态。</section></li></ul><h3 id="案例" data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/grass-green.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>案例</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">接下来，我们来实现一个简单的观察者模式的案例，案例情景，如下：</p><blockquote data-tool="mdnice编辑器" style="display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px; text-size-adjust: 100%; line-height: 1.55em; font-weight: 400; border-radius: 6px; color: #595959; font-style: normal; text-align: left; box-sizing: inherit; border-left: none; border: 1px solid #1b900d; background: #fff;"><span style="color: #74b56d; font-size: 34px; line-height: 1; font-weight: 700;">❝</span><p style="padding-top: 8px; padding-bottom: 8px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px; margin: 0px; color: black; line-height: 26px;">小明的水果店里的 🍊 非常甜，所以很快就卖光了，但是，接二连三的有顾客过来买 🍊，于是，小明告诉顾客：🍊已经卖完了，你们可以扫下这个公众号，订阅之后，等 🍊有货了会自动通知你们！</p><span style="float: right; color: #74b56d;">❞</span></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">这种场景我们就可以使用观察者模式，我们先创建一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observer</code> 接口(观察者)，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">interface</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Observer</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">update</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">再创建一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Customer</code> 客户类(具体的观察者)，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;实现&nbsp;Observer&nbsp;接口</span><br><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Customer</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Observer</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Customer</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;重新&nbsp;Observer&nbsp;的&nbsp;update()&nbsp;方法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">update</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“&nbsp;购买了&nbsp;🍊”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">之后创建一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable</code> 抽象类(被观察者)，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">abstract</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Observable</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;List&lt;Observer&gt;&nbsp;observers&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ArrayList&lt;&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;新增观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">add</span><span class="hljs-params" style="line-height: 26px;">(Observer&nbsp;observer)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observers.add(observer);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;移除观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">remove</span><span class="hljs-params" style="line-height: 26px;">(Observer&nbsp;observer)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observers.remove(observer);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;通知观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">abstract</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">notifyObservers</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">最后创建一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">FruitStore</code> 水果店类(具体的被观察者)，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;继承&nbsp;Observable&nbsp;类</span><br><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">FruitStore</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Observable</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;重写&nbsp;notifyObservers()&nbsp;通知所有的观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">notifyObservers</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span>&nbsp;(Observer&nbsp;observer:&nbsp;observers)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer.update();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.notifyObservers();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们再创建一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Client</code> 进行测试，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Client</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建一个水果店实例(被观察者)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FruitStore&nbsp;fruitStore&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;FruitStore();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fruitStore.add(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Customer(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>));&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;新增观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fruitStore.add(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Customer(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“per”</span>));&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;新增观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fruitStore.add(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Customer(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“zimu”</span>));&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;新增观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fruitStore.run();&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;通知所有观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;">lsy&nbsp;购买了&nbsp;🍊<br>per&nbsp;购买了&nbsp;🍊<br>zimu&nbsp;购买了&nbsp;🍊<br><br>Process&nbsp;finished&nbsp;with&nbsp;<span class="hljs-built_in" style="color: #c18401; line-height: 26px;">exit</span>&nbsp;code&nbsp;0<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可见，使用观察者模式可以降低观察者和被观察者之间的耦合性，可以建立一套触发机制；当然，Java JDK 已经提供了 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observer</code>、<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable</code>，我们使用它们同样可以实现功能。</p><h2 id="RxJava的使用" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #4CAF50;"><span class="prefix" style="display: none;"></span><span class="content" style="display: flex; color: #4CAF50; font-size: 20px;">RxJava的使用</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 20px; height: 10px; border-top-left-radius: 20px; border-top-right-radius: 20px; background: RGBA(76, 175, 80, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">通过上文的对观察者模式的理解之后，再来看看 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 是如何使用的，它同样有如下几个角色，如下：</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><strong style="color: #399003; font-weight: bold;"><span>「</span>Observable<span>」</span></strong> :被观察者，也就是事件的发生者<br><strong style="color: #399003; font-weight: bold;"><span>「</span>Observer<span>」</span></strong> :观察者，也就是事件的接受者<br><strong style="color: #399003; font-weight: bold;"><span>「</span>subscribe<span>」</span></strong> :两者产生订阅关系</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">具体的使用如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">UseRxJava</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建&nbsp;Observable&nbsp;被观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Observable&nbsp;observable&nbsp;=&nbsp;Observable.create(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObservableOnSubscribe&lt;String&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribe</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;ObservableEmitter&lt;String&gt;&nbsp;emitter)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Throwable&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍊&nbsp;到货了！”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“大家可以来买&nbsp;🍊&nbsp;了！”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onError(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Throwable(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍊&nbsp;又卖完了！”</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“WOW！🍊&nbsp;卖光了”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onComplete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onComplete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍊&nbsp;加急进货中…”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建&nbsp;Observer&nbsp;观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Observer&lt;String&gt;&nbsp;observer&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Observer&lt;String&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onSubscribe</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Disposable&nbsp;d)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onSubscribe:”</span>&nbsp;+&nbsp;d.isDisposed());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onNext</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;String&nbsp;s)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onNext:”</span>&nbsp;+&nbsp;s);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onError</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Throwable&nbsp;e)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onError:”</span>&nbsp;+&nbsp;e.getMessage());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onComplete</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onComplete”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;关联订阅关系</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observable.subscribe(observer);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;">onSubscribe:<span class="hljs-literal" style="color: #0184bb; line-height: 26px;">false</span><br>onNext:🍊&nbsp;到货了！<br>onNext:大家可以来买&nbsp;🍊&nbsp;了！<br>onError:🍊&nbsp;又卖完了！<br><br>BUILD&nbsp;SUCCESSFUL&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span>&nbsp;328ms<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">通过简单的使用案例和运行结果，可知：</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><strong style="color: #399003; font-weight: bold;"><span>「</span>onNext()<span>」</span></strong>: 可以多次发送事件<br><strong style="color: #399003; font-weight: bold;"><span>「</span>onComplete()<span>」</span></strong>: 可以多次调用不会报错，但只执行一次<br><strong style="color: #399003; font-weight: bold;"><span>「</span>onError()<span>」</span></strong>: 只能发送一次，多次调用会报错，不可和 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onComplete()</code> 共存<br><strong style="color: #399003; font-weight: bold;"><span>「</span>onComplete()<span>」</span></strong>: 和 <strong style="color: #399003; font-weight: bold;"><span>「</span>onError()<span>」</span></strong> 都存在时，只执行 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onError()</code><br><strong style="color: #399003; font-weight: bold;"><span>「</span>onComplete()<span>」</span></strong>: 和 <strong style="color: #399003; font-weight: bold;"><span>「</span>onError()<span>」</span></strong> 之后，观察者无法接收到发送事件<br><strong style="color: #399003; font-weight: bold;"><span>「</span>onSubscribe()<span>」</span></strong>: 是在订阅之后，发送事件之前执行</p><h2 id="RxJava编程思想" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #4CAF50;"><span class="prefix" style="display: none;"></span><span class="content" style="display: flex; color: #4CAF50; font-size: 20px;">RxJava编程思想</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 20px; height: 10px; border-top-left-radius: 20px; border-top-right-radius: 20px; background: RGBA(76, 175, 80, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 是在 Java 上的响应式扩展，通过使用可观察序列，用于组成异步和基于事件编程的类库，也就是以响应式编程思维来进行编程的Java类库。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">响应式编程是面向数据流的编程思想，在响应式编程思想下，一切皆数据流。响应式编程所侧重的是数据流的流动。</p><h3 id="响应式编程案例" data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/grass-green.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>响应式编程案例</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们在理解响应式编程之前，先来使用我们传统的思想实现一个加载网络图片的案例：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">MainActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“MainActivity”</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;ImageView&nbsp;imageView;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;String&nbsp;URL&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“<a href="https://cdn.lishaoy.net/serializable/serializable.png&quot;" target="_blank" rel="noopener">https://cdn.lishaoy.net/serializable/serializable.png&quot;</a></span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;Handler&nbsp;handler&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Handler(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Handler.Callback()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">handleMessage</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Message&nbsp;msg)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.i(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“handleMessage:&nbsp;aaa”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bitmap&nbsp;bitmap&nbsp;=&nbsp;(Bitmap)&nbsp;msg.obj;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imageView.setImageBitmap(bitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(loading&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;loading.dismiss();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;ProgressDialog&nbsp;loading;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_main);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imageView&nbsp;=&nbsp;findViewById(R.id.image_view);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">loadImage</span><span class="hljs-params" style="line-height: 26px;">(View&nbsp;view)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loading&nbsp;=&nbsp;ProgressDialog.show(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“”</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“loading”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Thread(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Runnable()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL&nbsp;url&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;URL(URL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpURLConnection&nbsp;urlConnection&nbsp;=&nbsp;(HttpURLConnection)&nbsp;url.openConnection();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;urlConnection.setConnectTimeout(<span class="hljs-number" style="color: #986801; line-height: 26px;">6000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;responseCode&nbsp;=&nbsp;urlConnection.getResponseCode();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>(responseCode&nbsp;==&nbsp;HttpURLConnection.HTTP_OK)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputStream&nbsp;inputStream&nbsp;=&nbsp;urlConnection.getInputStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bitmap&nbsp;bitmap&nbsp;=&nbsp;BitmapFactory.decodeStream(inputStream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Message&nbsp;message&nbsp;=&nbsp;handler.obtainMessage();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message.obj&nbsp;=&nbsp;bitmap;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler.sendMessage(message);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(MalformedURLException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}).start();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">如上，我们可能会用 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">new Thread()</code> 和 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Handler</code> 来实现或者其他方法，每个人的实现方法可能都不同。如果，使用 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 思想实现呢，必然实现方式要按照响应式编程数据流的编程思想，实现方式也就一致了。下面，我们再用 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 来实现它，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">MainActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“MainActivity”</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;ImageView&nbsp;imageView;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;String&nbsp;URL&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“<a href="https://cdn.lishaoy.net/image/112131.jpg&quot;" target="_blank" rel="noopener">https://cdn.lishaoy.net/image/112131.jpg&quot;</a></span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;ProgressDialog&nbsp;loading;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Disposable&nbsp;disposable;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_main);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imageView&nbsp;=&nbsp;findViewById(R.id.image_view);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">rxJavaLoadImage</span><span class="hljs-params" style="line-height: 26px;">(View&nbsp;view)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;Observable.just(URL)&nbsp;创建被观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Observable.just(URL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Function&lt;String,&nbsp;Bitmap&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;Bitmap&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">apply</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;s)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL&nbsp;url&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;URL(URL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpURLConnection&nbsp;urlConnection&nbsp;=&nbsp;(HttpURLConnection)&nbsp;url.openConnection();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;urlConnection.setConnectTimeout(<span class="hljs-number" style="color: #986801; line-height: 26px;">6000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;responseCode&nbsp;=&nbsp;urlConnection.getResponseCode();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(responseCode&nbsp;==&nbsp;HttpURLConnection.HTTP_OK)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputStream&nbsp;inputStream&nbsp;=&nbsp;urlConnection.getInputStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bitmap&nbsp;bitmap&nbsp;=&nbsp;BitmapFactory.decodeStream(inputStream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;bitmap;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribeOn(Schedulers.io())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;上面的代码分配工作线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.observeOn(AndroidSchedulers.mainThread())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;下面的代码分别UI线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;链式调用&nbsp;subscribe&nbsp;绑定观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Observer&lt;Bitmap&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;onSubscribe()&nbsp;方法在发送事件之前执行</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onSubscribe</span><span class="hljs-params" style="line-height: 26px;">(Disposable&nbsp;d)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loading&nbsp;=&nbsp;ProgressDialog.show(MainActivity.<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“”</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“loading”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable&nbsp;=&nbsp;d;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;onNext()&nbsp;在发送事件之后执行</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onNext</span><span class="hljs-params" style="line-height: 26px;">(Bitmap&nbsp;bitmap)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imageView.setImageBitmap(bitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onError</span><span class="hljs-params" style="line-height: 26px;">(Throwable&nbsp;e)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.e(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onError:&nbsp;”</span>&nbsp;+&nbsp;e.getMessage(),&nbsp;e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onComplete</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(loading&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;loading.dismiss();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onDestroy</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onDestroy();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable.dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">运行结果如图：</p><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/load_image.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 以链式调用，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable.just(URL)</code> 分发事件，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">map()</code> 来加工数据流(可以使用多个map处理不同的业务)、<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribeOn(Schedulers.io())</code> 切换工作线程、<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observeOn(AndroidSchedulers.mainThread())</code> 切换主线程，数据流流向 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribe</code> 进行处理(下游的处理根据上游的数据流变化而变化)。</p><h2 id="RxJava结合Retrofit使用(案例)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #4CAF50;"><span class="prefix" style="display: none;"></span><span class="content" style="display: flex; color: #4CAF50; font-size: 20px;">RxJava结合Retrofit使用(案例)</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 20px; height: 10px; border-top-left-radius: 20px; border-top-right-radius: 20px; background: RGBA(76, 175, 80, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">通过 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 使用，现在我们已经了解它的流式的响应式编程的思想，下面我们通过和 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Retrofit</code> 结合使用，看看是什么体验？</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们使用 <a href="https://www.wanandroid.com/blog/show/2" style="text-decoration: none; word-wrap: break-word; color: #399003; font-weight: normal; border-bottom: 1px solid #399003;" target="_blank" rel="noopener">玩Android 开放API</a> 来完成本次案例。首先我们使用 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Retrofit</code> 定义一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Api</code> 的接口，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">interface</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Api</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;获取项目分类数据</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@GET</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“project/tree/json”</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;">Observable&lt;ProjectBean&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getProject</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;获取项目列表数据(项目列表数据依赖于项目分类数据的id)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@GET</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“project/list/{pageIndex}/json”</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;">Observable&lt;ProjectItem&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getProjectItem</span><span class="hljs-params" style="line-height: 26px;">(@Path(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“pageIndex”</span>)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;pageIndex,&nbsp;@<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Query</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“cid”</span>)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;cid)</span>;<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ProjectBean</code>、<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ProjectItem</code> JavaBean 我们根据接口返回的数据利用工具 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">GsonFormat</code> 生成。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">然后，新建一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">HttpClient</code> 生成 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Retrofit</code>，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">HttpClient</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;api&nbsp;的&nbsp;base&nbsp;url</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;String&nbsp;BASE_URL&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“<a href="https://www.wanandroid.com/&quot;" target="_blank" rel="noopener">https://www.wanandroid.com/&quot;</a></span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setBaseUrl</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;baseUrl)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BASE_URL&nbsp;=&nbsp;baseUrl;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建&nbsp;Retrofit</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;Retrofit&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getRetrofit</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建&nbsp;OkHttp&nbsp;客户端</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OkHttpClient.Builder&nbsp;builder&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;OkHttpClient.Builder();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;配置参数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OkHttpClient&nbsp;httpClient&nbsp;=&nbsp;builder.addNetworkInterceptor(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;StethoInterceptor())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.readTimeout(<span class="hljs-number" style="color: #986801; line-height: 26px;">6666</span>,&nbsp;TimeUnit.SECONDS)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.connectTimeout(<span class="hljs-number" style="color: #986801; line-height: 26px;">6666</span>,&nbsp;TimeUnit.SECONDS)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.writeTimeout(<span class="hljs-number" style="color: #986801; line-height: 26px;">6666</span>,&nbsp;TimeUnit.SECONDS)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.build();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Retrofit.Builder().baseUrl(BASE_URL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.client(httpClient)&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;使用&nbsp;OkHttp&nbsp;访问网络</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.addConverterFactory(GsonConverterFactory.create(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Gson()))&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;设置&nbsp;json&nbsp;解析工具</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.addCallAdapterFactory(RxJava2CallAdapterFactory.create())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;设置&nbsp;rxjava</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.build();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">再新建一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RetrofitActivity</code> 来演示，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">RetrofitActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“RetrofitActivity”</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Api&nbsp;api;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;TextView&nbsp;textView;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;itemData;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Disposable&nbsp;disposable;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_retrofit);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textView&nbsp;=&nbsp;findViewById(R.id.text_view);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;api&nbsp;=&nbsp;HttpClient.getRetrofit().create(Api.class);&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;生成&nbsp;api</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;查询项目分类数据</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@SuppressLint</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“CheckResult”</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getProject</span><span class="hljs-params" style="line-height: 26px;">(View&nbsp;view)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable&nbsp;=&nbsp;api.getProject()&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;查询项目分类数据(返回的是&nbsp;Observable&nbsp;被观察者)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribeOn(Schedulers.io())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给上面的代码分配工作线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.observeOn(AndroidSchedulers.mainThread())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给下面的代码分配主线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Consumer&lt;ProjectBean&gt;()&nbsp;{&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;订阅并创建观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">accept</span><span class="hljs-params" style="line-height: 26px;">(ProjectBean&nbsp;projectBean)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textView.setText(projectBean.toString());&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;进行&nbsp;UI&nbsp;操作</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onDestroy</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onDestroy();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable.dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">运行结果如图：</p><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/get_project.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可见 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Retrofit</code> 和 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJava</code> 结合使用，代码量减少的同时，整个流程思路更为清晰，且可以为所欲为的切换线程。</p><h3 id="防抖" data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/grass-green.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>防抖</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们再使用防抖(防止用户操作带来的频繁发起请求问题)的方式来查询项目列表数据，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">RetrofitActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“RetrofitActivity”</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Api&nbsp;api;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;TextView&nbsp;textView;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;itemData;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Disposable&nbsp;disposable;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_retrofit);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textView&nbsp;=&nbsp;findViewById(R.id.text_view);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;api&nbsp;=&nbsp;HttpClient.getRetrofit().create(Api.class);&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;生成&nbsp;api</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getProjectItemData();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;…<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;查询项目列表数据，项目列表数据需要根据项目分类数据的&nbsp;id&nbsp;进行查询</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;且使用&nbsp;rxbinding&nbsp;增加防抖功能</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getProjectItemData</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Button&nbsp;button&nbsp;=&nbsp;findViewById(R.id.get_item_button_fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable&nbsp;=&nbsp;RxView.clicks(button)&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;设置防抖的&nbsp;view</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.throttleFirst(<span class="hljs-number" style="color: #986801; line-height: 26px;">2600</span>,&nbsp;TimeUnit.MILLISECONDS)&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;设置在&nbsp;2.6&nbsp;秒内只响应一次点击事件</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Consumer&lt;Object&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">accept</span><span class="hljs-params" style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable&nbsp;=&nbsp;api.getProject()&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;查询项目分类数据(返回的是&nbsp;Observable&nbsp;被观察者)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribeOn(Schedulers.io())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给上面的代码分配工作线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.observeOn(AndroidSchedulers.mainThread())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给下面的代码分配主线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Consumer&lt;ProjectBean&gt;()&nbsp;{&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;订阅并创建观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">accept</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;ProjectBean&nbsp;projectBean)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span>&nbsp;(ProjectBean.DataBean&nbsp;bean:&nbsp;projectBean.getData())&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable&nbsp;=&nbsp;api.getProjectItem(<span class="hljs-number" style="color: #986801; line-height: 26px;">1</span>,&nbsp;bean.getId())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;根据项目分类数据的&nbsp;id&nbsp;查询项目列表数据(返回的是&nbsp;Observable&nbsp;被观察者)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribeOn(Schedulers.io())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给上面的代码分配工作线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.observeOn(AndroidSchedulers.mainThread())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给下面的代码分配主线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Consumer&lt;ProjectItem&gt;()&nbsp;{&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;订阅并创建观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">accept</span><span class="hljs-params" style="line-height: 26px;">(ProjectItem&nbsp;projectItem)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.d(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“accept:&nbsp;”</span>&nbsp;+&nbsp;projectItem);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textView.setText(projectItem.toString());&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;进行&nbsp;UI&nbsp;操作</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onDestroy</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onDestroy();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable.dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">运行结果如图：</p><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/get_item.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可见以上代码虽然实现了防抖和嵌套查询的功能，但是，代码嵌套过多，难以维护。</p><h3 id="解决网络嵌套问题" data-tool="mdnice编辑器" style="padding: 0px; color: black; font-size: 17px; font-weight: bold; text-align: center; position: relative; margin-top: 20px; margin-bottom: 20px;"><span class="prefix" style="display: none;"></span><span class="content" style="color: #2b2b2b; padding-bottom: 2px;"><span style="width: 30px; height: 30px; display: block; background-image: url(https://files.mdnice.com/grass-green.png); background-position: center; background-size: 30px; margin: auto; opacity: 1; background-repeat: no-repeat; margin-bottom: -8px;"></span>解决网络嵌套问题</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">上面的代码虽没什么问题，但是嵌套太多，所以我们使用 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">flatMap</code> 操作符来解决此问题，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">RetrofitActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“RetrofitActivity”</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Api&nbsp;api;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;TextView&nbsp;textView;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;itemData;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Disposable&nbsp;disposable;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_retrofit);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textView&nbsp;=&nbsp;findViewById(R.id.text_view);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;api&nbsp;=&nbsp;HttpClient.getRetrofit().create(Api.class);&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;生成&nbsp;api</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getProjectItemData();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getItemData();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;…<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;查询项目列表数据，使用&nbsp;flatMap&nbsp;操作符，解决网络嵌套问题</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getItemData</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Button&nbsp;button&nbsp;=&nbsp;findViewById(R.id.get_item_button);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable&nbsp;=&nbsp;RxView.clicks(button)&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;设置防抖的&nbsp;view</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.throttleFirst(<span class="hljs-number" style="color: #986801; line-height: 26px;">2600</span>,&nbsp;TimeUnit.MILLISECONDS)&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;设置在&nbsp;2.6&nbsp;秒内只响应一次点击事件</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.observeOn(Schedulers.io())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给下面的代码分配工作线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.flatMap(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Function&lt;Object,&nbsp;ObservableSource&lt;ProjectBean&gt;&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;ObservableSource&lt;ProjectBean&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">apply</span><span class="hljs-params" style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;api.getProject();&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;查询项目分类数据，并且把数据流向下游</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.flatMap(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Function&lt;ProjectBean,&nbsp;ObservableSource&lt;ProjectBean.DataBean&gt;&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;ObservableSource&lt;ProjectBean.DataBean&gt;&nbsp;apply(ProjectBean&nbsp;projectBean)&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;Observable.fromIterable(projectBean.getData());&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;根据上游流过来的数据，迭代出每个&nbsp;ProjectItem&nbsp;项目列表数据，并且流向下游</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.flatMap(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Function&lt;ProjectBean.DataBean,&nbsp;ObservableSource&lt;ProjectItem&gt;&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;ObservableSource&lt;ProjectItem&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">apply</span><span class="hljs-params" style="line-height: 26px;">(ProjectBean.DataBean&nbsp;dataBean)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;api.getProjectItem(<span class="hljs-number" style="color: #986801; line-height: 26px;">1</span>,&nbsp;dataBean.getId());&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;根据上游流过来的数据，查询每个列表数据，并且流向下游</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.observeOn(AndroidSchedulers.mainThread())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Consumer&lt;ProjectItem&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">accept</span><span class="hljs-params" style="line-height: 26px;">(ProjectItem&nbsp;projectItem)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Exception&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;itemData&nbsp;+=&nbsp;projectItem.toString()&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“\n&nbsp;================================================&nbsp;\n”</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textView.setText(itemData);&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;根据上游流过来的数据，进行&nbsp;UI&nbsp;操作</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onDestroy</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onDestroy();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable.dispose();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">运行结果如图：</p><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/get_item1.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可见，使用 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">flatMap</code> 操作符后，思路更为清晰，代码平铺下来更易理解，以一种流式的方式不断的向下游流去数据，下游根据上游的数据可以决定是否继续向下游流或者做UI更新操作等，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">flatMap</code> 操作符可以重复使用，且线程的切换可以随意切换，这个就是 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJava</code> 数据流式的响应式编程思想。</p><h2 id="Hook钩子函数" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #4CAF50;"><span class="prefix" style="display: none;"></span><span class="content" style="display: flex; color: #4CAF50; font-size: 20px;">Hook钩子函数</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 20px; height: 10px; border-top-left-radius: 20px; border-top-right-radius: 20px; background: RGBA(76, 175, 80, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们已经了解了 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJava</code> 的思想和使用，现在我们来看看它的源码，我们从创建一个被观察者(Observable)开始 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable.create</code>，点击 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">create</code>，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;&lt;T&gt;&nbsp;<span class="hljs-function" style="line-height: 26px;">Observable&lt;T&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">create</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;ObservableOnSubscribe&lt;T&gt;&nbsp;source)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;Objects.requireNonNull(source,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“source&nbsp;is&nbsp;null”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;RxJavaPlugins.onAssembly(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObservableCreate&lt;&gt;(source));<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">很简单，就这么一句 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJavaPlugins.onAssembly()</code> 代码，我们点进 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onAssembly</code> 如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">/<strong><br>&nbsp;&nbsp;<em>&nbsp;Calls&nbsp;the&nbsp;associated&nbsp;hook&nbsp;function.<br>&nbsp;&nbsp;</em>&nbsp;<span class="hljs-doctag" style="color: #a626a4; line-height: 26px;">@param</span>&nbsp;&lt;T&gt;&nbsp;the&nbsp;value&nbsp;type<br>&nbsp;&nbsp;<em>&nbsp;<span class="hljs-doctag" style="color: #a626a4; line-height: 26px;">@param</span>&nbsp;source&nbsp;the&nbsp;hook’s&nbsp;input&nbsp;value<br>&nbsp;&nbsp;</em>&nbsp;<span class="hljs-doctag" style="color: #a626a4; line-height: 26px;">@return</span>&nbsp;the&nbsp;value&nbsp;returned&nbsp;by&nbsp;the&nbsp;hook<br>&nbsp;&nbsp;*/</strong></span><br><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@SuppressWarnings</span>({&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“rawtypes”</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“unchecked”</span>&nbsp;})<br><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@NonNull</span><br><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;&lt;T&gt;&nbsp;<span class="hljs-function" style="line-height: 26px;">Observable&lt;T&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onAssembly</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Observable&lt;T&gt;&nbsp;source)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;Function&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;Observable,&nbsp;?&nbsp;extends&nbsp;Observable&gt;&nbsp;f&nbsp;=&nbsp;onObservableAssembly;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(f&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;apply(f,&nbsp;source);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;source;<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可以看到注释：Calls the associated hook function(调用关联的钩子函数)，把 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onObservableAssembly</code> 赋值给了 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">f</code> 函数，我们通过查找 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onObservableAssembly</code> 发现他并没有赋值，也就是说它始终是 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">null</code>，所以，在没有给 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onObservableAssembly</code> 赋值的情况下，这个函数什么也不会做；所以，我们需要给 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onObservableAssembly</code> 函数赋值就可以先 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">if</code> 语句执行 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onObservableAssembly</code> 函数，那么怎么赋值呢？我们对 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onObservableAssembly</code> 进行搜索发现，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@SuppressWarnings</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“rawtypes”</span>)<br><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Nullable</span><br><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">volatile</span>&nbsp;Function&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;Observable,&nbsp;?&nbsp;extends&nbsp;Observable&gt;&nbsp;onObservableAssembly;<br><br>RxJavaPlugins.onObservableAssembly&nbsp;=&nbsp;onObservableAssembly;<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onObservableAssembly</code> 是 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJavaPlugins</code> 类的一个静态变量，于是我们就知道如何赋值了，我们用之前例子来测试，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">UseRxJava</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;给&nbsp;hook&nbsp;钩子函数赋值</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RxJavaPlugins.setOnObservableAssembly(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Function&lt;Observable,&nbsp;Observable&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;Observable&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">apply</span><span class="hljs-params" style="line-height: 26px;">(Observable&nbsp;observable)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Throwable&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(observable&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“&nbsp;你想买🍊&nbsp;？”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;observable;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;新增一个测试被观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Observable.just(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍊”</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Function&lt;String,&nbsp;Object&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;Object&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">apply</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;s)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Throwable&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy&nbsp;买了&nbsp;”</span>&nbsp;+&nbsp;s;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Consumer&lt;Object&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">accept</span><span class="hljs-params" style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Throwable&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(o);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建&nbsp;Observable&nbsp;被观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Observable&nbsp;observable&nbsp;=&nbsp;Observable.create(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObservableOnSubscribe&lt;String&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribe</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;ObservableEmitter&lt;String&gt;&nbsp;emitter)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Throwable&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍊&nbsp;到货了！”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“大家可以来买&nbsp;🍊&nbsp;了！”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onError(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Throwable(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍊&nbsp;又卖完了！”</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“WOW！🍊&nbsp;卖光了”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onComplete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onComplete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emitter.onNext(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍊&nbsp;加急进货中…”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建&nbsp;Observer&nbsp;观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Observer&lt;String&gt;&nbsp;observer&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Observer&lt;String&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onSubscribe</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Disposable&nbsp;d)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onSubscribe:”</span>&nbsp;+&nbsp;d.isDisposed());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onNext</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;String&nbsp;s)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onNext:”</span>&nbsp;+&nbsp;s);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onError</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Throwable&nbsp;e)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onError:”</span>&nbsp;+&nbsp;e.getMessage());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onComplete</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onComplete”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;关联订阅关系</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observable.subscribe(observer);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;">io.reactivex.rxjava3.internal.operators.observable.ObservableJust@694f9431&nbsp;你想买🍊&nbsp;？<br>io.reactivex.rxjava3.internal.operators.observable.ObservableMap@f2a0b8e&nbsp;你想买🍊&nbsp;？<br>lsy&nbsp;买了&nbsp;🍊<br>io.reactivex.rxjava3.internal.operators.observable.ObservableCreate@515f550a&nbsp;你想买🍊&nbsp;？<br>onSubscribe:<span class="hljs-literal" style="color: #0184bb; line-height: 26px;">false</span><br>onNext:🍊&nbsp;到货了！<br>onNext:大家可以来买&nbsp;🍊&nbsp;了！<br>onError:🍊&nbsp;又卖完了！<br><br>BUILD&nbsp;SUCCESSFUL&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span>&nbsp;868ms<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">hook</code> 钩子函数执行了3次，分别是：<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableJust</code>、<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableMap</code>、<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableCreate</code>，可知，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">hook</code> 钩子函数是一个全局监听函数，所以我们可以利用它做很多事情。</p><h2 id="RxJava观察者模式和标准观察者模式" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #4CAF50;"><span class="prefix" style="display: none;"></span><span class="content" style="display: flex; color: #4CAF50; font-size: 20px;">RxJava观察者模式和标准观察者模式</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 20px; height: 10px; border-top-left-radius: 20px; border-top-right-radius: 20px; background: RGBA(76, 175, 80, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们来继续解读源码，通过标准观察者模式和 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJava</code> 观察者模式的比较已区别来更加深刻的理解 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJava</code>。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">上面我们通过源码了解到 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">hook</code> 钩子函数，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable.create</code> 创建一个被观察者时如果我们给钩子函数赋值，就会先执行钩子函数；那么，我们的准观察者模式和 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 观察者模式有什么不同呢？</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">标准观察者模式有4个角色：Observable(被观察者)、ConcreteObservable(具体的被观察者)、Observer(观察者)、ConcreteObserver(具体的观察者)，<strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 观察者模式的这4个角色分别是什么呢？对应关系如下：</p><section class="table-container" data-tool="mdnice编辑器" style="overflow-x: auto;"><table style="display: table; text-align: left;"><thead><tr style="border: 0; border-top: 1px solid #ccc; background-color: white;"><th style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-weight: bold; background-color: #f0f0f0; font-size: 16px; color: #595959; min-width: 85px;">标准观察者模式</th><th style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-weight: bold; background-color: #f0f0f0; font-size: 16px; color: #595959; min-width: 85px;">RxJava观察者模式</th></tr></thead><tbody style="border: 0;"><tr style="border: 0; border-top: 1px solid #ccc; background-color: white;"><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">Observable (被观察者)</td><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">Observable 接口</td></tr><tr style="border: 0; border-top: 1px solid #ccc; background-color: #F8F8F8;"><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">ConcreteObservable(具体的被观察者)</td><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">Observable.create(）创建出来的，最终是一个 ObservableCreate 对象</td></tr><tr style="border: 0; border-top: 1px solid #ccc; background-color: white;"><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">Observer(观察者)</td><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">Observer 接口</td></tr><tr style="border: 0; border-top: 1px solid #ccc; background-color: #F8F8F8;"><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">ConcreteObserver(具体的观察者)</td><td style="border: 1px solid #ccc; padding: 5px 10px; text-align: left; font-size: 16px; color: #595959; min-width: 85px;">new Observer<string>() { } 创建出来的观察者对象</string></td></tr></tbody></table></section><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">在标准的观察者模式中 Observable (被观察者) 是持有 Observer(观察者) 列表的，那么 <strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 观察者模式呢？</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们来继续看源码，上文我们已经看过 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable.create</code>，点击 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">create</code>，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;&lt;T&gt;&nbsp;<span class="hljs-function" style="line-height: 26px;">Observable&lt;T&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">create</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;ObservableOnSubscribe&lt;T&gt;&nbsp;source)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;Objects.requireNonNull(source,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“source&nbsp;is&nbsp;null”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;RxJavaPlugins.onAssembly(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObservableCreate&lt;&gt;(source));<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">RxJavaPlugins.onAssembly()</code> 我们已经看过，我们来看看 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">new ObservableCreate&lt;&gt;(source)</code>， 这个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">source</code> 就是我们传进来的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">new ObservableOnSubscribe(){ … }</code>，我们点进 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableCreate</code> 如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ObservableCreate</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Observable</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;ObservableOnSubscribe&lt;T&gt;&nbsp;source;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">ObservableCreate</span><span class="hljs-params" style="line-height: 26px;">(ObservableOnSubscribe&lt;T&gt;&nbsp;source)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.source&nbsp;=&nbsp;source;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;…<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可知，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">return RxJavaPlugins.onAssembly(new ObservableCreate&lt;&gt;(source));</code> 返回的是 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableCreate(ObservableOnSubscribe&lt;T&gt; source) { this.source = source; }</code>，而 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">source</code> 是我们自己传进去的；</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们再来看看订阅 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observable.subscribe(observer)</code> 代码，点进 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribe</code>，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@SchedulerSupport</span>(SchedulerSupport.NONE)<br><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribe</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T&gt;&nbsp;observer)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;Objects.requireNonNull(observer,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“observer&nbsp;is&nbsp;null”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer&nbsp;=&nbsp;RxJavaPlugins.onSubscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>,&nbsp;observer);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Objects.requireNonNull(observer,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“The&nbsp;RxJavaPlugins.onSubscribe&nbsp;hook&nbsp;returned&nbsp;a&nbsp;null&nbsp;Observer.&nbsp;Please&nbsp;change&nbsp;the&nbsp;handler&nbsp;provided&nbsp;to&nbsp;RxJavaPlugins.setOnObservableSubscribe&nbsp;for&nbsp;invalid&nbsp;null&nbsp;returns.&nbsp;Further&nbsp;reading:&nbsp;<a href="https://github.com/ReactiveX/RxJava/wiki/Plugins&quot;" target="_blank" rel="noopener">https://github.com/ReactiveX/RxJava/wiki/Plugins&quot;</a></span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subscribeActual(observer);<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(NullPointerException&nbsp;e)&nbsp;{&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;NOPMD</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span>&nbsp;e;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(Throwable&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exceptions.throwIfFatal(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;can’t&nbsp;call&nbsp;onError&nbsp;because&nbsp;no&nbsp;way&nbsp;to&nbsp;know&nbsp;if&nbsp;a&nbsp;Disposable&nbsp;has&nbsp;been&nbsp;set&nbsp;or&nbsp;not</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;can’t&nbsp;call&nbsp;onSubscribe&nbsp;because&nbsp;the&nbsp;call&nbsp;might&nbsp;have&nbsp;set&nbsp;a&nbsp;Subscription&nbsp;already</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RxJavaPlugins.onError(e);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NullPointerException&nbsp;npe&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;NullPointerException(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Actually&nbsp;not,&nbsp;but&nbsp;can’t&nbsp;throw&nbsp;other&nbsp;exceptions&nbsp;due&nbsp;to&nbsp;RS”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;npe.initCause(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span>&nbsp;npe;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们看到重点代码 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribeActual(observer);</code> 点进 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribeActual</code> 如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">abstract</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribeActual</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T&gt;&nbsp;observer)</span></span>;<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">是一个抽象函数，也就说它的实现是在 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableCreate</code> 类里面的，因为，是 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableCreate</code> 调用了 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribe</code> 方法，那么，我们就回到 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribeActual</code> 类，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ObservableCreate</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Observable</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;ObservableOnSubscribe&lt;T&gt;&nbsp;source;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">ObservableCreate</span><span class="hljs-params" style="line-height: 26px;">(ObservableOnSubscribe&lt;T&gt;&nbsp;source)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.source&nbsp;=&nbsp;source;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;subscribeActual&nbsp;抽象方法的实现</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribeActual</span><span class="hljs-params" style="line-height: 26px;">(Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T&gt;&nbsp;observer)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;创建了一个&nbsp;CreateEmitter&nbsp;发射器，且传入了目标观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CreateEmitter&lt;T&gt;&nbsp;parent&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;CreateEmitter&lt;&gt;(observer);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;执行了目标观察者的&nbsp;onSubscribe&nbsp;方法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer.onSubscribe(parent);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;执行目标被观察者(ObservableCreate)&nbsp;的&nbsp;subscribe&nbsp;方法且传入了&nbsp;发射器&nbsp;CreateEmitter</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;source.subscribe(parent);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(Throwable&nbsp;ex)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exceptions.throwIfFatal(ex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent.onError(ex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可知，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribeActual</code> 抽象方法的实现：创建了一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">CreateEmitter</code> 发射器，且传入了目标观察者，且执行了目标观察者的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onSubscribe()</code> 方法；这就是为什么 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onSubscribe()</code> 方法会在订阅之后，发送事件之前执行的原因。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">之后，又执行目标被观察者(ObservableCreate) 的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribe</code> 方法且传入了发射器 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">CreateEmitter</code>，在看下我们的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">CreateEmitter</code> 发射器源码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">CreateEmitter</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AtomicReference</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Disposable</span>&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ObservableEmitter</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;,&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Disposable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">long</span>&nbsp;serialVersionUID&nbsp;=&nbsp;-<span class="hljs-number" style="color: #986801; line-height: 26px;">3434801548987643227L</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T&gt;&nbsp;observer;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;持有目标观察者&nbsp;observer</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CreateEmitter(Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T&gt;&nbsp;observer)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.observer&nbsp;=&nbsp;observer;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;执行&nbsp;onNext()&nbsp;会调用&nbsp;observer.onNext(t)&nbsp;执行目标观察者的&nbsp;onNext()</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onNext</span><span class="hljs-params" style="line-height: 26px;">(T&nbsp;t)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(t&nbsp;==&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onError(ExceptionHelper.createNullPointerException(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onNext&nbsp;called&nbsp;with&nbsp;a&nbsp;null&nbsp;value.”</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(!isDisposed())&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer.onNext(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onError</span><span class="hljs-params" style="line-height: 26px;">(Throwable&nbsp;t)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(!tryOnError(t))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RxJavaPlugins.onError(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;…<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">CreateEmitter</code> 发射器持有目标观察者 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observer</code> 目标观察者，执行 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onNext()</code> 会调用 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observer.onNext(t)</code> 执行目标观察者的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onNext()</code>。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 的观察者模式整个流程，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/observer_uml.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可见，<strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 观察者模式和标准的观察者模式完全不同，<strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 观察者模式的被观察者并没有持有观察者的列表，而是通过一个中间层 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">CreateEmitter</code> 发射器来完成事件的传递，它更像是一个发布订阅者模式，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/observable.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><h2 id="Map操作符原理" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; display: block; border-bottom: 4px solid #4CAF50;"><span class="prefix" style="display: none;"></span><span class="content" style="display: flex; color: #4CAF50; font-size: 20px;">Map操作符原理</span><span class="suffix" style="display: flex; box-sizing: border-box; width: 20px; height: 10px; border-top-left-radius: 20px; border-top-right-radius: 20px; background: RGBA(76, 175, 80, .5); color: rgb(255, 255, 255); font-size: 16px; letter-spacing: 0.544px; justify-content: flex-end; float: right; margin-top: -10px; box-sizing: border-box !important; overflow-wrap: break-word !important;"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">上文加载图片的案例里我们已经使用过 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">map</code> 操作符，用来把 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">String</code> 数据加工成 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Bitmap</code> 数据，从而流向下游，我们来回顾一下上文的案例代码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">rxJavaLoadImage</span><span class="hljs-params" style="line-height: 26px;">(View&nbsp;view)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;Observable.just(URL)&nbsp;创建被观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Observable.just(URL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;使用&nbsp;map&nbsp;操作符加工数据,从&nbsp;String&nbsp;转换为&nbsp;Bitmap</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Function&lt;String,&nbsp;Bitmap&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;Bitmap&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">apply</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;s)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL&nbsp;url&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;URL(URL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpURLConnection&nbsp;urlConnection&nbsp;=&nbsp;(HttpURLConnection)&nbsp;url.openConnection();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;urlConnection.setConnectTimeout(<span class="hljs-number" style="color: #986801; line-height: 26px;">6000</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;responseCode&nbsp;=&nbsp;urlConnection.getResponseCode();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(responseCode&nbsp;==&nbsp;HttpURLConnection.HTTP_OK)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputStream&nbsp;inputStream&nbsp;=&nbsp;urlConnection.getInputStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bitmap&nbsp;bitmap&nbsp;=&nbsp;BitmapFactory.decodeStream(inputStream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;bitmap;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;将&nbsp;Bitmap&nbsp;数据流向下游</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribeOn(Schedulers.io())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;上面的代码分配工作线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.observeOn(AndroidSchedulers.mainThread())&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;下面的代码分别UI线程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;链式调用&nbsp;subscribe&nbsp;绑定观察者</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Observer&lt;Bitmap&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;onSubscribe()&nbsp;方法在发送事件之前执行</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onSubscribe</span><span class="hljs-params" style="line-height: 26px;">(Disposable&nbsp;d)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loading&nbsp;=&nbsp;ProgressDialog.show(MainActivity.<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“”</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“loading”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;disposable&nbsp;=&nbsp;d;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;onNext()&nbsp;在发送事件之后执行</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onNext</span><span class="hljs-params" style="line-height: 26px;">(Bitmap&nbsp;bitmap)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imageView.setImageBitmap(bitmap);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onError</span><span class="hljs-params" style="line-height: 26px;">(Throwable&nbsp;e)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.e(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onError:&nbsp;”</span>&nbsp;+&nbsp;e.getMessage(),&nbsp;e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onComplete</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(loading&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;loading.dismiss();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">我们点进 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">map</code> 查看源码，如下</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@CheckReturnValue</span><br><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@SchedulerSupport</span>(SchedulerSupport.NONE)<br><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;&lt;R&gt;&nbsp;<span class="hljs-function" style="line-height: 26px;">Observable&lt;R&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">map</span><span class="hljs-params" style="line-height: 26px;">(Function&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T,&nbsp;?&nbsp;extends&nbsp;R&gt;&nbsp;mapper)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;ObjectHelper.requireNonNull(mapper,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“mapper&nbsp;is&nbsp;null”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;RxJavaPlugins.onAssembly(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObservableMap&lt;T,&nbsp;R&gt;(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>,&nbsp;mapper));<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">可知，最终返回的是一个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableMap</code>，点进 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableMap</code> 如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ObservableMap</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>,&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">U</span>&gt;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AbstractObservableWithUpstream</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>,&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">U</span>&gt;&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;Function&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T,&nbsp;?&nbsp;extends&nbsp;U&gt;&nbsp;function;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">ObservableMap</span><span class="hljs-params" style="line-height: 26px;">(ObservableSource&lt;T&gt;&nbsp;source,&nbsp;Function&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T,&nbsp;?&nbsp;extends&nbsp;U&gt;&nbsp;function)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>(source);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.function&nbsp;=&nbsp;function;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;new&nbsp;MapObserver&lt;T,&nbsp;U&gt;(t,&nbsp;function)&nbsp;把我们传进来的函数包上了一次&nbsp;MapObserver</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribeActual</span><span class="hljs-params" style="line-height: 26px;">(Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;U&gt;&nbsp;t)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;source.subscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;MapObserver&lt;T,&nbsp;U&gt;(t,&nbsp;function));<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;…<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">关键代码 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">source.subscribe(new MapObserver&lt;T, U&gt;(t, function))</code> 把我们传进来的函数包上了一次 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">MapObserver</code>，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">source.subscribe()</code> 这个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">source</code> 就是 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable</code>，就相当于 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observable.subscribe()</code>，而 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">MapObserver</code> 继承与 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">BasicFuseableObserver</code>，<code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">BasicFuseableObserver</code> 实现了 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">Observer</code>，最终 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">source.subscribe()</code> 会执行到：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;">&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@SchedulerSupport</span>(SchedulerSupport.NONE)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribe</span><span class="hljs-params" style="line-height: 26px;">(@NonNull&nbsp;Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T&gt;&nbsp;observer)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Objects.requireNonNull(observer,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“observer&nbsp;is&nbsp;null”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer&nbsp;=&nbsp;RxJavaPlugins.onSubscribe(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>,&nbsp;observer);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Objects.requireNonNull(observer,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“The&nbsp;RxJavaPlugins.onSubscribe&nbsp;hook&nbsp;returned&nbsp;a&nbsp;null&nbsp;Observer.&nbsp;Please&nbsp;change&nbsp;the&nbsp;handler&nbsp;provided&nbsp;to&nbsp;RxJavaPlugins.setOnObservableSubscribe&nbsp;for&nbsp;invalid&nbsp;null&nbsp;returns.&nbsp;Further&nbsp;reading:&nbsp;<a href="https://github.com/ReactiveX/RxJava/wiki/Plugins&quot;" target="_blank" rel="noopener">https://github.com/ReactiveX/RxJava/wiki/Plugins&quot;</a></span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;是一个静态抽象方法，最终由实现类完成</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subscribeActual(observer);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(NullPointerException&nbsp;e)&nbsp;{&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;NOPMD</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span>&nbsp;e;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(Throwable&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exceptions.throwIfFatal(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;can’t&nbsp;call&nbsp;onError&nbsp;because&nbsp;no&nbsp;way&nbsp;to&nbsp;know&nbsp;if&nbsp;a&nbsp;Disposable&nbsp;has&nbsp;been&nbsp;set&nbsp;or&nbsp;not</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;can’t&nbsp;call&nbsp;onSubscribe&nbsp;because&nbsp;the&nbsp;call&nbsp;might&nbsp;have&nbsp;set&nbsp;a&nbsp;Subscription&nbsp;already</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RxJavaPlugins.onError(e);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NullPointerException&nbsp;npe&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;NullPointerException(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Actually&nbsp;not,&nbsp;but&nbsp;can’t&nbsp;throw&nbsp;other&nbsp;exceptions&nbsp;due&nbsp;to&nbsp;RS”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;npe.initCause(e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span>&nbsp;npe;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribeActual(observer)</code> 是一个静态抽象方法，最终由实现类完成，也就是 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">ObservableJust</code> 的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">subscribeActual</code> 方法，如下</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ObservableJust</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Observable</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ScalarSupplier</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;T&nbsp;value;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">ObservableJust</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;T&nbsp;value)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.value&nbsp;=&nbsp;value;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;source.subscribe(new&nbsp;MapObserver&lt;T,&nbsp;U&gt;(t,&nbsp;function));&nbsp;又对&nbsp;observer&nbsp;包裹了一层</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">subscribeActual</span><span class="hljs-params" style="line-height: 26px;">(Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T&gt;&nbsp;observer)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScalarDisposable&lt;T&gt;&nbsp;sd&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ScalarDisposable&lt;&gt;(observer,&nbsp;value);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer.onSubscribe(sd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sd.run();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;T&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">get</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;value;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observer</code> 是我们从 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">source.subscribe</code> 里 传进来的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">MapObserver</code>，而此段代码又对 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observer</code> 包裹了一层。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observer.onSubscribe(sd)</code> 就会执行我们自己 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">new</code> 出来的目标观察者的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onSubscribe</code> 里的逻辑。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">sd.run()</code> 进去看看源码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ScalarDisposable</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AtomicInteger</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">QueueDisposable</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt;,&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Runnable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;…<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(get()&nbsp;==&nbsp;START&nbsp;&amp;&amp;&nbsp;compareAndSet(START,&nbsp;ON_NEXT))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;observer&nbsp;就是我们传进来的&nbsp;MapObserver</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer.onNext(value);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(get()&nbsp;==&nbsp;ON_NEXT)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lazySet(ON_COMPLETE);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;observer.onComplete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observer.onNext(value)</code> 的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">observer</code> 就是我们传进来的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">MapObserver</code>，也就是执行 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">MapObserver.onNext()</code>，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">MapObserver</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>,&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">U</span>&gt;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">BasicFuseableObserver</span>&lt;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>,&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">U</span>&gt;&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;Function&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T,&nbsp;?&nbsp;extends&nbsp;U&gt;&nbsp;mapper;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MapObserver(Observer&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;U&gt;&nbsp;actual,&nbsp;Function&lt;?&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>&nbsp;T,&nbsp;?&nbsp;extends&nbsp;U&gt;&nbsp;mapper)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>(actual);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.mapper&nbsp;=&nbsp;mapper;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onNext</span><span class="hljs-params" style="line-height: 26px;">(T&nbsp;t)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(done)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(sourceMode&nbsp;!=&nbsp;NONE)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;downstream.onNext(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U&nbsp;v;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v&nbsp;=&nbsp;Objects.requireNonNull(mapper.apply(t),&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“The&nbsp;mapper&nbsp;function&nbsp;returned&nbsp;a&nbsp;null&nbsp;value.”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(Throwable&nbsp;ex)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail(ex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;downstream.onNext(v);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;…<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">onNext()</code> 通过 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">mapper.apply(t)</code> 对我们的数据进行转换，如下</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; padding: 0px; border-radius: 6px; background: white;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; letter-spacing: 0px; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">interface</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Function</span>&lt;@<span class="hljs-title" style="color: #c18401; line-height: 26px;">NonNull</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>,&nbsp;@<span class="hljs-title" style="color: #c18401; line-height: 26px;">NonNull</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">R</span>&gt;&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>&nbsp;Apply&nbsp;some&nbsp;calculation&nbsp;to&nbsp;the&nbsp;input&nbsp;value&nbsp;and&nbsp;return&nbsp;some&nbsp;other&nbsp;value.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</em>&nbsp;<span class="hljs-doctag" style="color: #a626a4; line-height: 26px;">@param</span>&nbsp;t&nbsp;the&nbsp;input&nbsp;value<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>&nbsp;<span class="hljs-doctag" style="color: #a626a4; line-height: 26px;">@return</span>&nbsp;the&nbsp;output&nbsp;value<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</em>&nbsp;<span class="hljs-doctag" style="color: #a626a4; line-height: 26px;">@throws</span>&nbsp;Throwable&nbsp;if&nbsp;the&nbsp;implementation&nbsp;wishes&nbsp;to&nbsp;throw&nbsp;any&nbsp;type&nbsp;of&nbsp;exception<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;">R&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">apply</span><span class="hljs-params" style="line-height: 26px;">(T&nbsp;t)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;Throwable</span>;<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;">传入 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">T</code> 返回 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">R</code>，而这个 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">apply</code> 会执行我们实现的重写 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">apply</code> 方法的里面逻辑。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 的 <code style="font-size: 14px; word-wrap: break-word; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #0bb712; background: rgba(127, 226, 159, 0.48); display: inline-block; padding: 0 2px; border-radius: 2px; height: 21px; line-height: 22px; box-shadow: none;">map</code> 操作符使用了装饰器模式，在不影响主数据流的情况下，对需要加工的数据进行包装，在自己的包装类里完成数据的加工。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #2b2b2b; margin: 10px 0px; letter-spacing: 2px; font-size: 16px; word-spacing: 2px;"><strong style="color: #399003; font-weight: bold;"><span>「</span>RxJava<span>」</span></strong> 里的操作符非常多，只要你理解其中的几个的原理，其它的操作符原理都差不多，下面列出了基本所有的操作符，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; display: block; flex-direction: column; justify-content: center; align-items: center;"><img src="https://cdn.lishaoy.net/rxjava/rxjava.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div></section>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/rxjava.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>解读Android中的序列化与Json解析</title>
      <link>https://h.lishaoy.net/serializable.html</link>
      <guid>https://h.lishaoy.net/serializable.html</guid>
      <pubDate>Mon, 03 Aug 2020 18:16:13 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/serializable/serializable.png&quot; class=&quot;full-image&quot; alt=&quot;Serializable&quot; title=&quot;Serializable&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;section id=&quot;nice&quot; data-tool=&quot;mdnice编辑器&quot; data-website=&quot;https://www.mdnice.com&quot; style=&quot;font-size: 16px; padding: 0 10px; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; line-height: 1.75; color: #595959; font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light; letter-spacing: 2px; background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%); background-size: 20px 20px; background-position: center center;&quot;&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;&quot;&gt;我们在日常工作中，网络数据传输最主流的的格式就是 &lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;&quot;&gt;json&lt;/code&gt;，我们常使用 &lt;strong style=&quot;color: #595959; font-weight: bold;&quot;&gt;&lt;span&gt;「&lt;/span&gt;Gson&lt;span&gt;」&lt;/span&gt;&lt;/strong&gt; 开源框架来处理它，那么它们是如何工作的呢？本篇文章将解读 &lt;strong style=&quot;color: #595959; font-weight: bold;&quot;&gt;&lt;span&gt;「&lt;/span&gt;Android&lt;span&gt;」&lt;/span&gt;&lt;/strong&gt; 中的序列化与 &lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;&quot;&gt;json&lt;/code&gt; 解析，如：Java 语言提供的 &lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;&quot;&gt;Serializable&lt;/code&gt;、Android 提供的 &lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;&quot;&gt;Parceable&lt;/code&gt;。&lt;/p&gt;
&lt;hr data-tool=&quot;mdnice编辑器&quot; style=&quot;margin: 0; margin-top: 10px; margin-bottom: 10px; height: 1px; padding: 0; border: none; border-top: 2px solid #d9b8fa;background: none;&quot;&gt;&lt;/section&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/serializable/serializable.png" class="full-image" alt="Serializable" title="Serializable"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; padding: 0 10px; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; line-height: 1.75; color: #595959; font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light; letter-spacing: 2px; background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%); background-size: 20px 20px; background-position: center center;"><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们在日常工作中，网络数据传输最主流的的格式就是 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">json</code>，我们常使用 <strong style="color: #595959; font-weight: bold;"><span>「</span>Gson<span>」</span></strong> 开源框架来处理它，那么它们是如何工作的呢？本篇文章将解读 <strong style="color: #595959; font-weight: bold;"><span>「</span>Android<span>」</span></strong> 中的序列化与 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">json</code> 解析，如：Java 语言提供的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code>、Android 提供的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parceable</code>。</p><hr data-tool="mdnice编辑器" style="margin: 0; margin-top: 10px; margin-bottom: 10px; height: 1px; padding: 0; border: none; border-top: 2px solid #d9b8fa;background: none;"></section><a id="more"></a><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; padding: 0 10px; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; line-height: 1.75; color: #595959; font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light; letter-spacing: 2px; background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%); background-size: 20px 20px; background-position: center center;"><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">本篇文章的示例代码放在 <a href="https://github.com/persilee/android_practice" style="text-decoration: none; word-wrap: break-word; color: #664D9D; font-weight: normal; border-bottom: 1px solid #664D9D;" target="_blank" rel="noopener">Github</a> 上，所有知识点，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/serializable/serializable.xmind1.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><h2 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; text-align: left; margin: 20px 10px 0px 0px;"><span class="prefix" style="display: none;"></span><span class="content" style="font-size: 18px; font-weight: bold; display: inline-block; padding-left: 10px; border-left: 5px solid #DEC6FB; color: #595959;">序列化的定义及相关概念</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">在系统的底层，数据传输形式是字节序列形式传输，系统并不认识对象，只认识字节序列，那么我们如何进行进程间的通讯，我们需要先将数据序列化，就是将对象传化为字节序列；反序列化，当底层的字节序列传输到相应的进程时，就需要反序列化，就是字节序列转化为对象。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">在进程间通讯、本地数据存储、网络数据传输都离不开序列化，对于不同的场景选择合适的序列化方案对应用的性能有着极大的影响。</p><h3 id="序列化" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">序列化</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">数据结构或对象转换成二进制的过程，就是将数据结构或对象转换成可以存储或者传输的数据格式的过程。</p><h3 id="反序列化" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">反序列化</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">二进制串转换成数据结构或对象的过程，就是序列化生成的二进制串数据被还原成数据结构或对象的过程。</p><h3 id="序列化和反序列化的目的" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">序列化和反序列化的目的</span><span class="suffix" style="display: none;"></span></h3><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; font-size: 15px; color: #595959; list-style-type: circle;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">序列化：主要用于网络传输，数据持久化，一般序列化也称为编码(Encode)</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">反序列化：主要用于从网络，磁盘上读取字节数组还原成原始对象，一般反序列化也称为解码 (Decode)</section></li></ul><h2 id="Serializable接口" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; text-align: left; margin: 20px 10px 0px 0px;"><span class="prefix" style="display: none;"></span><span class="content" style="font-size: 18px; font-weight: bold; display: inline-block; padding-left: 10px; border-left: 5px solid #DEC6FB; color: #595959;">Serializable接口</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 是 Java 提供的序列化接口，它非常简单，如下</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">interface</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Serializable</span>&nbsp;</span>{<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 只是一个标记，用来被 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">ObjectOutputStream</code> 序列化，被 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">ObjectInputStream</code> 反序列化。</p><h3 id="Serializable的使用" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Serializable的使用</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们先来新建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Student</code> 类，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Student</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Serializable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">long</span>&nbsp;serialVersionUID&nbsp;=&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">7911650650846382143L</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;Integer&nbsp;age;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;List&lt;Course&gt;&nbsp;courses;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;获取课程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;List&lt;Course&gt;&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getCourses</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;courses;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;新增课程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">addCourse</span><span class="hljs-params" style="line-height: 26px;">(Course&nbsp;course)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.courses.add(course);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;用&nbsp;transient&nbsp;关键字标记的成员变量不参与序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">transient</span>&nbsp;Date&nbsp;createTime;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;静态成员变量属于类而不属于对象，也不参与序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;SimpleDateFormat&nbsp;dateFormat&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;SimpleDateFormat();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Student</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name,&nbsp;Integer&nbsp;age)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.age&nbsp;=&nbsp;age;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;courses&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ArrayList&lt;&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createTime&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Date();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getName</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setName</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;Integer&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getAge</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;age;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setAge</span><span class="hljs-params" style="line-height: 26px;">(Integer&nbsp;age)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.age&nbsp;=&nbsp;age;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Student{“</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name=’”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘\’’</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“,&nbsp;age=”</span>&nbsp;+&nbsp;age&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“,&nbsp;courses=”</span>&nbsp;+&nbsp;courses&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“,&nbsp;createTime=”</span>&nbsp;+&nbsp;createTime&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘}’</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Student</code> 类依赖于 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Course</code> 类，所以，我们再新建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Course</code> 类，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Course</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Serializable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">long</span>&nbsp;serialVersionUID&nbsp;=&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">7980496416494451794L</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Course</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name,&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getName</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setName</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getScore</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setScore</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Course{“</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name=’”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘\’’</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“,&nbsp;score=”</span>&nbsp;+&nbsp;score&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘}’</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们再新建一个序列化的工具类，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">SerializableUtil</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;String&nbsp;path&nbsp;=&nbsp;System.getProperty(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“user.dir”</span>)&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“/serializable/src/main/java/net/lishaoy/serializable/serializable/out/student.out”</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">synchronized</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">boolean</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">serializable</span><span class="hljs-params" style="line-height: 26px;">(Object&nbsp;o)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(o&nbsp;==&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream&nbsp;outputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectOutputStream(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;FileOutputStream(path));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.writeObject(o);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“序列化成功！”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(SecurityException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(outputStream&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">false</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">synchronized</span>&nbsp;&lt;T&gt;&nbsp;<span class="hljs-function" style="line-height: 26px;">T&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">reverseSerializable</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectInputStream&nbsp;inputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectInputStream(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;FileInputStream(path));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;object&nbsp;=&nbsp;inputStream.readObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“反序列化成功！\n”</span>&nbsp;&nbsp;+&nbsp;object);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;(T)&nbsp;object;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(Exception&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(inputStream&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inputStream.close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">SerializableUtil</code> 工具类，来序列化和反序列化我们的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Student</code>，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">UseSerializable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Student&nbsp;student&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Student(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>,&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.addCourse(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“英语”</span>,<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SerializableUtil.serializable(student);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;反序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SerializableUtil.reverseSerializable();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">序列化成功！<br>反序列化成功！<br>Student{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘lsy’</span>,&nbsp;age=66,&nbsp;courses=[Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘英语’</span>,&nbsp;score=66.0}],&nbsp;createTime=null}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">在使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 时，可以发现以下几个特点：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; font-size: 15px; color: #595959; list-style-type: circle;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">需要现象 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 的类，才可以序列化和反序列化</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">transient</code> 关键字标记的成员变量不参与序列化</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">静态成员变量不参与序列化</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; font-size: 14px; font-weight: normal; color: #595959;">一个实现序列化的类，它的子类也是可序列化的</section></li></ul><h3 id="serialVersionUID与兼容性" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">serialVersionUID与兼容性</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><strong style="color: #595959; font-weight: bold;"><span>「</span>serialVersionUID的作用：<span>」</span></strong> 用来表明类的不同版本间的兼容性。Java 序列化机制会通过判断类的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">serialVersionUID</code> 来验证版本一致性；在反序列化时，JVM 会把传来的字节流中的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">serialVersionUID</code> 与本地相应实体类的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">serialVersionUID</code> 进行比较。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><strong style="color: #595959; font-weight: bold;"><span>「</span>兼容性问题：<span>」</span></strong> 为了在反序列化时，确保类版本的兼容性，最好在每个要序列化的类中加入 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">private static final long serialVersionUID = XXX</code> 属性。如果不显式定义该属性，这个属 性值将由JVM根据类的相关信息计算，而修改后的类的计算 结果与修改前的类的计算结果往往不 同，从而造成对象的反序列化因为类版本不兼容而失败。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">serialVersionUID</code> 可以用 Android Studio 自动生成，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/serializable/UID1.png" alt="no-shadow" title="UID" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">使用时按 <kbd>option</kbd> + <kbd>enter</kbd>，如图：</p><div style="width: 66%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/serializable/UID.png" alt="no-shadow" title="UID" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><h3 id="Externalizable接口" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Externalizable接口</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">JDK 提供了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 接口外还提供了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Externalizable</code> 接口，它继承了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code>，优先级高于 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code>，源码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">interface</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Externalizable</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Serializable</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">writeExternal</span><span class="hljs-params" style="line-height: 26px;">(ObjectOutput&nbsp;var1)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">readExternal</span><span class="hljs-params" style="line-height: 26px;">(ObjectInput&nbsp;var1)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException,&nbsp;ClassNotFoundException</span>;<br>}<br></code></pre><h3 id="Externalizable接口的使用" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Externalizable接口的使用</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们来看下简单的使用，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Course</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Externalizable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">long</span>&nbsp;serialVersionUID&nbsp;=&nbsp;-<span class="hljs-number" style="color: #986801; line-height: 26px;">342346458732794596L</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Course</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Course</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name,&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Course:&nbsp;”</span>&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name&nbsp;”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“&nbsp;score&nbsp;”</span>&nbsp;+&nbsp;score);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getName</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setName</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getScore</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setScore</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">writeExternal</span><span class="hljs-params" style="line-height: 26px;">(ObjectOutput&nbsp;objectOutput)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“writeExternal&nbsp;…”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;objectOutput.writeObject(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">readExternal</span><span class="hljs-params" style="line-height: 26px;">(ObjectInput&nbsp;objectInput)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException,&nbsp;ClassNotFoundException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“readExternal&nbsp;…”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;(String)&nbsp;objectInput.readObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Course{“</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name=’”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘\’’</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“,&nbsp;score=”</span>&nbsp;+&nbsp;score&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘}’</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException,&nbsp;ClassNotFoundException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Course&nbsp;course&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“数学”</span>,<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayOutputStream&nbsp;byteArrayOutputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ByteArrayOutputStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream&nbsp;outputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectOutputStream(byteArrayOutputStream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.writeObject(course);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">byte</span>[]&nbsp;bytes&nbsp;=&nbsp;byteArrayOutputStream.toByteArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.close();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;反序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectInputStream&nbsp;inputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectInputStream(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ByteArrayInputStream(bytes));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println((Course)inputStream.readObject());<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">Course:&nbsp;name&nbsp;数学&nbsp;score&nbsp;66.0<br>writeExternal&nbsp;…<br>readExternal&nbsp;…<br>Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘数学’</span>,&nbsp;score=0.0}<br><br>BUILD&nbsp;SUCCESSFUL&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span>&nbsp;576ms<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">可以看到，在序列化时会调用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">writeExternal</code> 方法，反序列化时会调用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">readExternal</code> 方法，也就是说我们可以灵活的控制想序列化的字段。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Externalizable</code> 接口，在反序列化时，需要写默认的空构造函数，否则报错：<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">InvalidClassException</code>。</p><h3 id="使用Serializable的注意点" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">使用Serializable的注意点</span><span class="suffix" style="display: none;"></span></h3><h4 id="readObject和writeObject" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 18px;"><span class="prefix" style="display: none;"></span><span class="content">readObject和writeObject</span><span class="suffix" style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">readObject</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">writeObject</code> 并没有在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 接口里定义，但是通过查看源码，可知，如：<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">ObjectOutputStream</code> 点击进入源码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><br>…<br><br><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">writeSerialData</span><span class="hljs-params" style="line-height: 26px;">(Object&nbsp;var1,&nbsp;ObjectStreamClass&nbsp;var2)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;ClassDataSlot[]&nbsp;var3&nbsp;=&nbsp;var2.getClassDataLayout();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span>(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;var4&nbsp;=&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>;&nbsp;var4&nbsp;&lt;&nbsp;var3.length;&nbsp;++var4)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectStreamClass&nbsp;var5&nbsp;=&nbsp;var3[var4].desc;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;hasWriteObjectMethod&nbsp;会判断我们是否重写了&nbsp;writeObject()&nbsp;方法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(var5.hasWriteObjectMethod())&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream.PutFieldImpl&nbsp;var6&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.curPut;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.curPut&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SerialCallbackContext&nbsp;var7&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.curContext;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(extendedDebugInfo)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.debugInfoStack.push(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“custom&nbsp;writeObject&nbsp;data&nbsp;(class&nbsp;\””</span>&nbsp;+&nbsp;var5.getName()&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“\”)”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.curContext&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;SerialCallbackContext(var1,&nbsp;var5);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.bout.setBlockDataMode(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">true</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;通过反射执行&nbsp;writeObject()&nbsp;方法</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var5.invokeWriteObject(var1,&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.bout.setBlockDataMode(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">false</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.bout.writeByte(<span class="hljs-number" style="color: #986801; line-height: 26px;">120</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">finally</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.curContext.setUsed();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.curContext&nbsp;=&nbsp;var7;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(extendedDebugInfo)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.debugInfoStack.pop();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.curPut&nbsp;=&nbsp;var6;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">else</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.defaultWriteFields(var1,&nbsp;var5);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br>…<br><br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">所以，我们也可以像 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Externalizable</code> 接口提供的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">writeExternal</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">readExternal</code> 方法一样使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">readObject</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">writeObject</code> 来灵活的序列化和反序列化。例如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ReadWriteObjectCourse</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Serializable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">long</span>&nbsp;serialVersionUID&nbsp;=&nbsp;-<span class="hljs-number" style="color: #986801; line-height: 26px;">6828110073372979297L</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">ReadWriteObjectCourse</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">ReadWriteObjectCourse</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name,&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Course:&nbsp;”</span>&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name&nbsp;”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“&nbsp;score&nbsp;”</span>&nbsp;+&nbsp;score);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getName</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setName</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getScore</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">setScore</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;重写&nbsp;writeObject()&nbsp;方法，只序列化&nbsp;name&nbsp;字段</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">writeObject</span><span class="hljs-params" style="line-height: 26px;">(ObjectOutputStream&nbsp;outputStream)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“writeObject&nbsp;…”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.writeObject(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;重写&nbsp;readObject()&nbsp;方法，只反序列化&nbsp;name&nbsp;字段</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">readObject</span><span class="hljs-params" style="line-height: 26px;">(ObjectInputStream&nbsp;inputStream)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException,&nbsp;ClassNotFoundException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“readObject&nbsp;…”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;(String)&nbsp;inputStream.readObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Course{“</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name=’”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘\’’</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“,&nbsp;score=”</span>&nbsp;+&nbsp;score&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘}’</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException,&nbsp;ClassNotFoundException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReadWriteObjectCourse&nbsp;course&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ReadWriteObjectCourse(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“数学”</span>,<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayOutputStream&nbsp;byteArrayOutputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ByteArrayOutputStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream&nbsp;outputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectOutputStream(byteArrayOutputStream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.writeObject(course);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">byte</span>[]&nbsp;bytes&nbsp;=&nbsp;byteArrayOutputStream.toByteArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.close();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;反序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectInputStream&nbsp;inputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectInputStream(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ByteArrayInputStream(bytes));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ReadWriteObjectCourse&nbsp;course1&nbsp;=&nbsp;(ReadWriteObjectCourse)&nbsp;inputStream.readObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(course1);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">Course:&nbsp;name&nbsp;数学&nbsp;score&nbsp;66.0<br>writeObject&nbsp;…<br>readObject&nbsp;…<br>Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘数学’</span>,&nbsp;score=0.0}<br><br>BUILD&nbsp;SUCCESSFUL&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span>&nbsp;722ms<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">readObject</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">writeObject</code> 方法都被执行，自定义序列化 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">name</code> 字段。<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 接口除了这2个方法可以重写外，还有2个方法，分别是 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">readResolve</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">writeReplace</code>。</p><h4 id="多引用写入" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 18px;"><span class="prefix" style="display: none;"></span><span class="content">多引用写入</span><span class="suffix" style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><strong style="color: #595959; font-weight: bold;"><span>「</span>多引用写入<span>」</span></strong> 问题，我们来看如下代码：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException,&nbsp;ClassNotFoundException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;Course&nbsp;course&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“数学”</span>,<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayOutputStream&nbsp;byteArrayOutputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ByteArrayOutputStream();<br>&nbsp;&nbsp;&nbsp;&nbsp;ObjectOutputStream&nbsp;outputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectOutputStream(byteArrayOutputStream);<br>&nbsp;&nbsp;&nbsp;&nbsp;outputStream.writeObject(course);<br>&nbsp;&nbsp;&nbsp;&nbsp;course.setName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“英语”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;outputStream.writeObject(course);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">byte</span>[]&nbsp;bytes&nbsp;=&nbsp;byteArrayOutputStream.toByteArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;outputStream.close();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;反序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;ObjectInputStream&nbsp;inputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ObjectInputStream(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ByteArrayInputStream(bytes));<br>&nbsp;&nbsp;&nbsp;&nbsp;Course&nbsp;course1&nbsp;=&nbsp;(Course)&nbsp;inputStream.readObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;Course&nbsp;course2&nbsp;=&nbsp;(Course)&nbsp;inputStream.readObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(course1);<br>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(course2);<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">Course:&nbsp;name&nbsp;数学&nbsp;score&nbsp;66.0<br>writeExternal&nbsp;…<br>readExternal&nbsp;…<br>Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘数学’</span>,&nbsp;score=0.0}<br>Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘数学’</span>,&nbsp;score=0.0}<br><br>BUILD&nbsp;SUCCESSFUL&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span>&nbsp;557ms<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">course.setName(“英语”);</code> 把 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">name</code> 修改后，重新 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">outputStream.writeObject(course);</code> 但是结果并没有改变。这个就是多引用问题：对于一个实例的多个引用，为了节省空间，只会写入一次。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">我们可以使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">outputStream.reset();</code> 来解决问题。</p><h4 id="子类实现 Serializable，而父类没有实现 Serializable" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 18px;"><span class="prefix" style="display: none;"></span><span class="content">子类实现 Serializable，而父类没有实现 Serializable</span><span class="suffix" style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><strong style="color: #595959; font-weight: bold;"><span>「</span>子类实现 Serializable，而父类没有实现 Serializable<span>」</span></strong> 问题，我们新建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Person</code> 类，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Person</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Person</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Person{“</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name=’”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘\’’</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘}’</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">让 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Student</code> 类继承它，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Student</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Person</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Serializable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;…<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Student</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name,&nbsp;Integer&nbsp;age)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.age&nbsp;=&nbsp;age;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;courses&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;ArrayList&lt;&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createTime&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Date();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Student:&nbsp;name:”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“&nbsp;age:”</span>&nbsp;+&nbsp;age&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“&nbsp;createTime:”</span>&nbsp;+&nbsp;createTime);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;…<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">java.io.InvalidClassException:&nbsp;net.lishaoy.serializable.serializable.Student;&nbsp;no&nbsp;valid&nbsp;constructor<br>…<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">提示我们没有构造函数，我们需要在父类 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Person</code> 加入无参的构造函数，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Person</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;加入无参构造函数</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Person</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Person</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Person{“</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name=’”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘\’’</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘}’</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><h4 id="单例模式的序列化" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 18px;"><span class="prefix" style="display: none;"></span><span class="content">单例模式的序列化</span><span class="suffix" style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">序列化会导致单例失效，就是序列化前后会产生多个对象，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Single</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">SingleClass</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Serializable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">long</span>&nbsp;serialVersionUID&nbsp;=&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">9153534024695280942L</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">boolean</span>&nbsp;flag&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">false</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;SingleClass&nbsp;singleClass;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;SingleClass&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">getInstance</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>(singleClass&nbsp;==&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">synchronized</span>&nbsp;(SingleClass.class)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(singleClass&nbsp;==&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleClass&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;SingleClass();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;singleClass;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">SingleClass</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>&nbsp;(!flag)&nbsp;flag&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">true</span>;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">else</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;RuntimeException(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“单例模式被侵犯！”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SingleClass&nbsp;singleClass&nbsp;=&nbsp;SingleClass.getInstance();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SerializableUtil.serializable(singleClass);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;反序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SingleClass&nbsp;singleClass1&nbsp;=&nbsp;SerializableUtil.reverseSerializable();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“序列化之前：”</span>&nbsp;+&nbsp;singleClass.hashCode());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“序列化之后：”</span>+&nbsp;singleClass1.hashCode());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">序列化成功！<br>反序列化成功！<br>net.lishaoy.serializable.serializable.Single<span class="hljs-variable" style="color: #986801; line-height: 26px;">$SingleClass</span>@41629346<br>序列化之前：1442407170<br>序列化之后：1096979270<br><br>BUILD&nbsp;SUCCESSFUL&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span>&nbsp;621ms<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">单例模式序列化前后会产生多个对象的问题，可以重写 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">readResolve()</code> 方法解决。</p><h2 id="Parcelable接口" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; text-align: left; margin: 20px 10px 0px 0px;"><span class="prefix" style="display: none;"></span><span class="content" style="font-size: 18px; font-weight: bold; display: inline-block; padding-left: 10px; border-left: 5px solid #DEC6FB; color: #595959;">Parcelable接口</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code> 是 Android SDK 为我们提供的序列化接口，它是基于内存的，由于内存读写速度高于硬盘，因此 Android 中的跨进程对象的传输一般使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code>；<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code> 相对于 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 的使用复杂一些，但是 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code> 的效率比 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 也高很多</p><h3 id="Parcelable的使用" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Parcelable的使用</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">由于 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code> 是 Android SDK 提供的，所以，需要在 Android 工程下使用，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Course</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">Parcelable</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Course”</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;String&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;String&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“Course{“</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name=’”</span>&nbsp;+&nbsp;name&nbsp;+&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘\’’</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“,&nbsp;score=”</span>&nbsp;+&nbsp;score&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘}’</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Course</span><span class="hljs-params" style="line-height: 26px;">(Parcel&nbsp;in)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;in.readString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;in.readFloat();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;反序列化，将&nbsp;Parcel&nbsp;对象转换为&nbsp;Parcelable</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;Creator&lt;Course&gt;&nbsp;CREATOR&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Creator&lt;Course&gt;()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//反序列化的方法，将Parcel还原成Java对象</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;Course&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">createFromParcel</span><span class="hljs-params" style="line-height: 26px;">(Parcel&nbsp;in)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course(in);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//提供给外部类反序列化这个数组使用。</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;Course[]&nbsp;newArray(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;size)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course[size];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;};<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">Course</span><span class="hljs-params" style="line-height: 26px;">(String&nbsp;name,&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>&nbsp;score)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name&nbsp;=&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score&nbsp;=&nbsp;score;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">describeContents</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span>&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;序列化，将对象转换成一个&nbsp;Parcel&nbsp;对象</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">writeToParcel</span><span class="hljs-params" style="line-height: 26px;">(Parcel&nbsp;dest,&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;flags)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dest.writeString(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dest.writeFloat(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.score);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">MainActivity</code> 里通过 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Intent</code> 来传递数据，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">MainActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_main);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Course.runParcel();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Button&nbsp;button&nbsp;=&nbsp;findViewById(R.id.button);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;button.setOnClickListener(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;View.OnClickListener()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onClick</span><span class="hljs-params" style="line-height: 26px;">(View&nbsp;v)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Intent&nbsp;intent&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Intent(MainActivity.<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>,&nbsp;ParcelActivity.class);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;intent.putExtra(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“course”</span>,&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“数学”</span>,&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">66f</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;startActivity(intent);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">ParcelActivity</code> 接受数据并打印，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">ParcelActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“ParcelActivity”</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_parcel);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Intent&nbsp;intent&nbsp;=&nbsp;getIntent();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Parcelable&nbsp;course&nbsp;=&nbsp;intent.getParcelableExtra(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“course”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.i(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“onCreate:&nbsp;”</span>&nbsp;+&nbsp;course.toString());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">I/ParcelActivity:&nbsp;onCreate:&nbsp;Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘数学’</span>,&nbsp;score=66.0}<br></code></pre><h2 id="Parcelable与Serializable的性能比较" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; text-align: left; margin: 20px 10px 0px 0px;"><span class="prefix" style="display: none;"></span><span class="content" style="font-size: 18px; font-weight: bold; display: inline-block; padding-left: 10px; border-left: 5px solid #DEC6FB; color: #595959;">Parcelable与Serializable的性能比较</span><span class="suffix"></span></h2><h3 id="Serializable性能分析" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Serializable性能分析</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code> 是 Java 中的序列化接口，其使用起来简单但开销较大(因为 Serializable 在序列化过程中使用了反射机制，故而会产生大量的临时变量，从而导致频繁的GC)，并且在读写数据过程中，它是通 过IO流的形式将数据写入到硬盘或者传输到网络上。</p><h3 id="Parcelable性能分析" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Parcelable性能分析</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code> 则是以 IBinder 作为信息载体，在内存上开销比较小，因此在内存之间进行数据传递时，推荐使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code>，而 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Parcelable</code> 对数据进行持久化或者网络传输时操作复杂，一般这个时候推荐使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Serializable</code>。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; text-align: left; margin: 20px 10px 0px 0px;"><span class="prefix" style="display: none;"></span><span class="content" style="font-size: 18px; font-weight: bold; display: inline-block; padding-left: 10px; border-left: 5px solid #DEC6FB; color: #595959;">JSON解析方式</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式，通常用于：数据标记，存储，传输。</p><h3 id="Android Studio自带org.json解析" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Android Studio自带org.json解析</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">org.json 解析是基于文档驱动，需要把全部文件读入到内存中，然后遍历所有数据，根据需要检索想要 的数据，具体使用，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">OrgJsonActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“OrgJsonActivity”</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_org_json);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createJson();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parseJson();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(JSONException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span>&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">createJson</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;JSONException,&nbsp;IOException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;file&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;File(getFilesDir(),&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“orgJson.json”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSONObject&nbsp;student&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;JSONObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.put(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.put(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“age”</span>,&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSONObject&nbsp;course&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;JSONObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;course.put(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“数学”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;course.put(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“score”</span>,<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.put(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“course”</span>,&nbsp;course);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSONArray&nbsp;courses&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;JSONArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;courses.put(<span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>,&nbsp;course);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.put(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“courses”</span>,courses);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileOutputStream&nbsp;outputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;FileOutputStream(file);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.write(student.toString().getBytes());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outputStream.close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.i(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“createJson:&nbsp;”</span>&nbsp;+&nbsp;student.toString());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">parseJson</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>&nbsp;IOException,&nbsp;JSONException&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;file&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;File(getFilesDir(),&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“orgJson.json”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileInputStream&nbsp;inputStream&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;FileInputStream(file);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputStreamReader&nbsp;streamReader&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;InputStreamReader(inputStream);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BufferedReader&nbsp;reader&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;BufferedReader(streamReader);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;line;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer&nbsp;stringBuffer&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;StringBuffer();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">while</span>&nbsp;((line&nbsp;=&nbsp;reader.readLine())&nbsp;!=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringBuffer.append(line);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inputStream.close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;streamReader.close();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader.close();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Student&nbsp;student&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Student();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSONObject&nbsp;jsonObject&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;JSONObject(stringBuffer.toString());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name&nbsp;=&nbsp;jsonObject.optString(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;age&nbsp;=&nbsp;jsonObject.optInt(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“age”</span>,&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.setName(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.setAge(age);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSONArray&nbsp;courses&nbsp;=&nbsp;jsonObject.optJSONArray(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“courses”</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span>&nbsp;(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>&nbsp;i&nbsp;=&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>;&nbsp;i&nbsp;&lt;&nbsp;courses.length();&nbsp;i++)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSONObject&nbsp;course&nbsp;=&nbsp;courses.getJSONObject(i);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Course&nbsp;course1&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;course1.setName(course.optString(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“”</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;course1.setScore((<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">float</span>)&nbsp;course.optDouble(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“score”</span>,&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.addCourse(course1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.i(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“parseJson:&nbsp;”</span>&nbsp;+&nbsp;student);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">I/OrgJsonActivity:&nbsp;createJson:&nbsp;{<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>:<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“age”</span>:<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“course”</span>:{<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>:<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“数学”</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“score”</span>:<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>},<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“courses”</span>:[{<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>:<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“数学”</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“score”</span>:<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>}]}<br>I/OrgJsonActivity:&nbsp;parseJson:&nbsp;Student{id=<span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>,&nbsp;name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘lsy’</span>,&nbsp;age=<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>,&nbsp;courses=[Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘数学’</span>,&nbsp;score=<span class="hljs-number" style="color: #986801; line-height: 26px;">66.0</span>}]}<br></code></pre><h3 id="Gson解析" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Gson解析</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">Gson 解析也是基于事件驱动，它根据所需取的数据 建立1个对应于JSON数据的JavaBean类，即可通过简单操作解析出 所需数据，具体使用如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">GsonActivity</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span>&nbsp;<span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span>&nbsp;</span>{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span>&nbsp;String&nbsp;TAG&nbsp;=&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“GsonActivity”</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle&nbsp;savedInstanceState)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setContentView(R.layout.activity_gson);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createGson();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span>&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span>&nbsp;<span class="hljs-title" style="color: #4078f2; line-height: 26px;">createGson</span><span class="hljs-params" style="line-height: 26px;">()</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Student&nbsp;student&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Student(<span class="hljs-number" style="color: #986801; line-height: 26px;">1</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>,&nbsp;<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.addCourse(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Course(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“英语”</span>,<span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gson&nbsp;gson&nbsp;=&nbsp;<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span>&nbsp;Gson();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;json&nbsp;=&nbsp;gson.toJson(student);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.i(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“createGson:&nbsp;json&nbsp;”</span>&nbsp;+&nbsp;json);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//&nbsp;反序列化</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.i(TAG,&nbsp;<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“createGson:&nbsp;json1”</span>&nbsp;+&nbsp;gson.fromJson(json,&nbsp;Student.class));<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; background: #fafafa;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; border-radius: 0px; font-size: 12px; -webkit-overflow-scrolling: touch; box-shadow: none;">I/GsonActivity:&nbsp;createGson:&nbsp;json&nbsp;{<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“age”</span>:66,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“courses”</span>:[{<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>:<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“英语”</span>,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“score”</span>:66.0}],<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“id”</span>:1,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>:<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>}<br>I/GsonActivity:&nbsp;createGson:&nbsp;json1Student{id=1,&nbsp;name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘lsy’</span>,&nbsp;age=66,&nbsp;courses=[Course{name=<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘英语’</span>,&nbsp;score=66.0}]}<br></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">Json 解析方式还有 Jackson 解析、Fastjson解析等，在此就不具体介绍。</p><h2 id="Gson原理解析" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; text-align: left; margin: 20px 10px 0px 0px;"><span class="prefix" style="display: none;"></span><span class="content" style="font-size: 18px; font-weight: bold; display: inline-block; padding-left: 10px; border-left: 5px solid #DEC6FB; color: #595959;">Gson原理解析</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">在序列化和反序列化的过程中，<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">Gson</code> 充当了一个解析器的角色，如图</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/serializable/gson.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><h3 id="JsonElement" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">JsonElement</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">该类是一个抽象类，代表着 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #595959; box-shadow: none;">json</code> 串的某一个元素。这个元素可以是一个 Json(JsonObject)、可以是一个数组(JsonArray)、可以是一个Java的基本类型( JsonPrimitive)、当然也可以为null( JsonNull)；JsonObject、JsonArray、JsonPrimitive、JsonNull 都是 JsonElement 这个抽象类的子类。JsonElement 提供了一系列的方法来判断当前的JsonElement。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">JsonObject 对象可以看成 name/values 的集合，而这写 values 就是一个个 JsonElement，他们的结构可以 用如下图表示:</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/serializable/json3.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div><h3 id="Gson的工作流程" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; color: black; font-size: 16px; font-weight: bold; text-align: center;"><span class="prefix" style="display: none;"></span><span class="content" style="border-bottom: 2px solid #DEC6FB; color: #595959;">Gson的工作流程</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: #595959; margin: 10px 0px; letter-spacing: 2px; font-size: 14px; word-spacing: 2px;">Gson的工作流程，如图</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/serializable/gson2.png" alt="no-shadow" style="max-width: 100%; border-radius: 6px; display: block; margin: 20px auto; object-fit: contain;"></figure></div></section>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/serializable.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Android并发编程你了解多少</title>
      <link>https://h.lishaoy.net/thread-concurrent.html</link>
      <guid>https://h.lishaoy.net/thread-concurrent.html</guid>
      <pubDate>Fri, 31 Jul 2020 05:49:17 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/thread-concurrent/concurrent1.png&quot; class=&quot;full-image&quot; alt=&quot;concurrent&quot; title=&quot;concurrent&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;section id=&quot;nice&quot; data-tool=&quot;mdnice编辑器&quot; data-website=&quot;https://www.mdnice.com&quot; style=&quot;font-size: 16px; color: black; padding: 0 10px; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#39;PingFang SC&#39;, Cambria, Cochin, Georgia, Times, &#39;Times New Roman&#39;, serif;&quot;&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: &#39;Helvetica Neue&#39;, Helvetica, &#39;Segoe UI&#39;, Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;&quot;&gt;对于 &lt;strong style=&quot;font-weight: bold; color: black;&quot;&gt;Android&lt;/strong&gt; 开发人员来说，并发编程知识的使用并不是那么频繁(相对于 &lt;strong style=&quot;font-weight: bold; color: black;&quot;&gt;Java&lt;/strong&gt; 开发者而言)，但是，我们想写一些框架或者阅读开源框架源码都需要掌握并发编程的相关知识，而且，并发编程相关知识也是面试高频问题之一，所以，我们也要全面的掌握并发编程知识，本篇文章将从浅入深概述并发编程知识。&lt;/p&gt;
&lt;hr data-tool=&quot;mdnice编辑器&quot; style=&quot;height: 1px; margin: 0; margin-top: 10px; margin-bottom: 10px; border: none; border-top: 1px solid black;background: white;&quot;&gt;&lt;/section&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/thread-concurrent/concurrent1.png" class="full-image" alt="concurrent" title="concurrent"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; color: black; padding: 0 10px; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">对于 <strong style="font-weight: bold; color: black;">Android</strong> 开发人员来说，并发编程知识的使用并不是那么频繁(相对于 <strong style="font-weight: bold; color: black;">Java</strong> 开发者而言)，但是，我们想写一些框架或者阅读开源框架源码都需要掌握并发编程的相关知识，而且，并发编程相关知识也是面试高频问题之一，所以，我们也要全面的掌握并发编程知识，本篇文章将从浅入深概述并发编程知识。</p><hr data-tool="mdnice编辑器" style="height: 1px; margin: 0; margin-top: 10px; margin-bottom: 10px; border: none; border-top: 1px solid black;background: white;"></section><a id="more"></a><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; color: black; padding: 0 10px; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">本篇文章的示例代码放在 <a href="https://github.com/persilee/android_practice" style="text-decoration: none; word-wrap: break-word; font-weight: bold; color: rgb(239, 112, 96); border-bottom: 1px solid rgb(239, 112, 96);" target="_blank" rel="noopener">Github</a> 上，所有知识点，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/concurrent.xmind.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><h2 id="基础概念" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">基础概念</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在理解并发编程之前，我需要理解一些相关的基本概念，我们先从一些相关的基本概念开始。</p><h3 id="CPU核心数和线程数的关系" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">CPU核心数和线程数的关系</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><strong style="font-weight: bold; color: black;">多核心:</strong> 是物理上的，单核、双核、多核，指的就是物理核心的数目。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><strong style="font-weight: bold; color: black;">多线程:</strong> 是逻辑上的，简单的说就是模拟出的 CPU 核心数；</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><strong style="font-weight: bold; color: black;">核心数和线程数的关系:</strong> 目前主流 CUP 有双核、三核和四核，增加核心数目就是为了增加线程数,因为操作系统是通过线程来执行任务的，一般情况下它们是1:1对应关系，也就是说四核CPU一般拥有四个线程。但 Intel 引入超线程技术后,使核心数与线程数形成1:2的关系。</p><h3 id="CPU时间片轮转机制" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">CPU时间片轮转机制</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们平时在开发的时候，感觉并没有受cpu核心数的限制，想启动线程就启动线程，哪怕是在单核CPU上，为什么？这是因为操作系统提供了一种CPU时间片轮转机制。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">时间片轮转调度是一种最古老、最简单、最公平且使用最广的算法,又称RR(Round-Robin，RR)调度。根据先进先出原则，排成队列(就绪队列)，调度时，将 CPU 分配给队首进程，让其执行一个时间段(称为：时间片)，时间片通常为 10-100ms 数量级，当执行的时间片用完时，会由计时器发出时钟中断请求，调度程序便据此来停止该进程的执行，并将它排到队列末尾，然后再把 CPU 重新分配给当前队列的队首进程，同理如此往复。</p><h3 id="什么是进程和线程" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">什么是进程和线程</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><strong style="font-weight: bold; color: black;">进程是程序运行资源分配的最小单位</strong>，其中资源包括：CPU、内存空间、磁盘等,同一进程中的多个线程共享该进程中的全部系统资源，而进程和进程之间是相互独立的。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">进程是程序在计算机上的一次执行活动。当你运行一个程序，你就启动了一个进程。显然，程序是死的、静态的，进程是活的、动态的。进程可以分为系统进程和用户进程，凡是用于完成操作系统的各种功能的进程就是系统进程，它们就是处于运行状态下的操作系统本身，用户进程就是所有由你启动的进程。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><strong style="font-weight: bold; color: black;">线程是 CPU 调度的最小单位,必须依赖于进程而存在</strong>，线程是进程的一个实体，是 CPU 调度和分派的基本单位，它是比进程更小的、能独立运行的基本单位。线程自己基本上不拥有系统资源，只拥有一点在运行中必不可少的资源(如程序计数器，一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">并行和并发</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们举个例子，如果有条高速公路A上面并排有6条车道，公路中间有个收费站，那么，在某一时刻，同时通过此收费站的，就是并行；在单位时间内通过此收收费站的，就是并发。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">当谈论并发的时候一定要加个单位时间，也就是说单位时间内并发量是多少，离开了单位时间其实是没有意义的。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><strong style="font-weight: bold; color: black;">并行:</strong> ：指在同一时刻，有多条指令在多个处理器上同时执行。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><strong style="font-weight: bold; color: black;">并发:</strong> ：指在同一时刻只能有一条指令执行，但多个进程指令被快速的轮换执行，使得在宏观上具有多个进程同时执行的效果，但在微观上并不是同时执行的，只是把时间分成若干段，使多个进程快速交替的执行。</p><h2 id="线程(Thread)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">线程(Thread)</span><span class="suffix"></span></h2><h3 id="线程的启动" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">线程的启动</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">启动线程的方式有，如下：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">className extends Thread，重新 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">run()</code> 方法</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">className implements Runnable，然后，由 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Thread</code> 运行</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">className implements Callable，然后，由 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Thread</code> 运行</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">NewThread</span> </span>{  <span class="hljs-comment" style="color: #75715e; line-height: 26px;">    // 继承 Thread，重写 run() 方法  </span>   <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">UseThread</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">super</span>.run();<span><span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“extends Thread”</span>);<span>        }<span>    }    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">     // 实现 Runnable 接口    </span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">UseRunnable</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">implements</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Runnable</span> </span>{<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“implements Runnable”</span>);<span>        }<span>    }   <span class="hljs-comment" style="color: #75715e; line-height: 26px;">     // 实现 Callable 接口     </span>  <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">UseCallable</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">implements</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Callable</span>&lt;<span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">String</span>&gt; </span>{<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> String <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">call</span><span class="hljs-params" style="line-height: 26px;">()</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> Exception </span>{<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“implements Callable”</span>);<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">return</span> <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“return UseCallable”</span>;<span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> ExecutionException, InterruptedException </span>{<span>        <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 使用 Thread 创建线程</span><span>        UseThread useThread = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> UseThread();<span>        useThread.start();<span>        <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 使用 Runnable 创建线程</span><span>        UseRunnable useRunnable = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> UseRunnable();<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread(useRunnable).start();<span>        <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 使用 Callable 创建线程</span><span>        UseCallable useCallable = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> UseCallable();<span>        FutureTask&lt;String&gt; task = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> FutureTask&lt;&gt;(useCallable);<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread(task).start();<span>        System.out.println(task.get()); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 通过 get 获取返回结果</span><span><span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">extends Thread<span>implements Runnable<span>implements Callable<span><span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> UseCallable<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 753ms<span></span></span></span></span></span></span></code></pre><blockquote data-tool="mdnice编辑器" style="display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; border-left: 3px solid rgba(0, 0, 0, 0.4); padding-top: 10px; padding-bottom: 10px; border-left-color: rgb(221, 221, 221); margin-top: 1.2em; margin-bottom: 1.2em; padding-right: 1em; padding-left: 1em; border-left-width: 4px; color: rgb(119, 119, 119); quotes: none; border-left-color: black; background: rgba(0, 0, 0, .16);"><p style="padding-top: 8px; padding-bottom: 8px; box-sizing: border-box; margin-bottom: 16px; text-align: start; white-space: normal; text-size-adjust: auto; margin: 0px; font-size: 15px; font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif; color: rgb(119, 119, 119); line-height: 1.75em;">继承 Thread 的方式和实现 Runnable 的方式，执行完成后无法返回结果，实现 Callable 的方式，执行完成后可以返回结果。 (md partial supported)</p></blockquote><h3 id="run()和start()的区别" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">run()和start()的区别</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们通过 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">new Thread()</code> 只是 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">new</code> 出一个 Thread 的示例，并没有和操作系统中的真正的线程挂钩，只有执行 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">start()</code> 方法后，才真正的启动线程。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">进入 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">start()</code> 方法查看源码，可得知最终是调用了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">private native void start0()</code>，是一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">native</code> 方法，是由 C 或 C++ 来操作系统(分配CPU等操作)，之后才调用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">run()</code> 方法，且 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">start()</code> 方法不能重复调用。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">run()</code> 只是类的一个成员方法，和普通方法并无区别，可重复执行(如单独执行 run() 方法，并不会启动线程)。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">示例代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">StartRunMethod</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">StartAndRun</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“run: This is “</span> + Thread.currentThread().getName()); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 获取当前线程名</span><span>        }<span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">runMethod</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“runMethod: This is “</span> + Thread.currentThread().getName()); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 获取当前线程名</span><span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span><span>        StartAndRun startAndRun = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> StartAndRun();<span>        startAndRun.setName(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“ThreadRun”</span>); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 设置线程的名字</span><span>        startAndRun.start(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 真正启动名字为 ThreadRun 的线程</span><span>        startAndRun.run();  <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 只是一个普通方法，和 runMethod() 没有区别</span><span>        startAndRun.runMethod(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 输出结果和 startAndRun.run() 相同</span><span><span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">run: This is main<span>runMethod: This is main<span>run: This is ThreadRun<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 381ms<span></span></span></span></span></span></code></pre><h3 id="线程的中止" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">线程的中止</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">线程的终止，要么是 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">run()</code> 执行完成，要么是抛出异常导致线程结束，我们也可以手动的中止线程，线程 Thread 的 API 给我们提供了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">stop()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">resume()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">suspend()</code> 方法，但是，他们都被标记为 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">@deprecated</code>，也就是过期的，不建议使用，因为这些方法，在调用后，线程不会释放已经占有的资源，所以，容易导致死锁问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">安全的中止线程，我们可以用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">interrupt()</code> 方法，此方法是一种协作的，也就是说它只是发送一个中断信号，不代表线程会立即停止，需要线程通过 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">isInterrupted()</code> 方法进行判断是否中止线程。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">示例代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">InterruptThread</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">MyThread</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            String threadName = Thread.currentThread().getName(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 获取当前线程名</span><span>           <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">while</span> (!isInterrupted()) { <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 判断是否需要中止</span><span>               System.out.println(threadName + <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“ running …”</span>);<span>           }<span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> InterruptedException </span>{<span><span>        MyThread myThread = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> MyThread();<span>        myThread.start();<span>        Thread.sleep(<span class="hljs-number" style="line-height: 26px;">6</span>);<span>        myThread.interrupt(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 发出中断信号</span><span><span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h3 id="按顺序执行线程" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">按顺序执行线程</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们来新建一个类，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">JoinThread</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">JoinMethod</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span><span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> Thread thread;<span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">JoinMethod</span><span class="hljs-params" style="line-height: 26px;">(Thread thread)</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.thread = thread;<span>        }<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; <span class="hljs-number" style="line-height: 26px;">6</span>; i++) {<span>                System.out.println(thread.getName() + <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“ running … “</span>);<span>            }<span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> InterruptedException </span>{<span><span>        JoinMethod joinMethod = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> JoinMethod(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread());<span>        JoinMethod joinMethod1 = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> JoinMethod(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread());<span>        joinMethod.start();<span>        joinMethod1.start();<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Thread-0 running … <span>Thread-0 running … <span>Thread-0 running … <span>Thread-2 running … <span>Thread-2 running … <span>Thread-2 running … <span>Thread-0 running … <span>Thread-0 running … <span>Thread-0 running … <span>Thread-2 running … <span>Thread-2 running … <span>Thread-2 running … <span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 213ms<span>2 actionable tasks: 1 executed, 1 up-to-date<span>11:31:47 PM: Task execution finished <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">‘JoinThread.main()’</span>.<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">发现，joinMethod 线程和 joinMethod1 线程是随机交替执行的，那么如何让它们按顺序执行呢，我们可以使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">join()</code> 方法，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> InterruptedException </span>{<span><span>    JoinMethod joinMethod = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> JoinMethod(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread());<span>    JoinMethod joinMethod1 = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> JoinMethod(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread());<span>    joinMethod.start();<span>    joinMethod.join(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 使用 join() 方法，由 joinMethod 执行完成之后才让出执行权</span><span>    joinMethod1.start();<span><span>}<span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">输出结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Thread-0 running … <span>Thread-0 running … <span>Thread-0 running … <span>Thread-0 running … <span>Thread-0 running … <span>Thread-0 running … <span>Thread-2 running … <span>Thread-2 running … <span>Thread-2 running … <span>Thread-2 running … <span>Thread-2 running … <span>Thread-2 running … <span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 482ms<span>2 actionable tasks: 2 executed<span>11:36:17 PM: Task execution finished <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">‘JoinThread.main()’</span>.<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h3 ="线程的状态"="" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">线程的状态</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在 Java 中线程的状态分为 6 中：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">初始(NEW)：新创建了一个线程对象，但还没有调用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">start()</code> 方法。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">运行(RUNNABLE)：Java 线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后，其他线程(比如main线程)调用了该对象的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">start()</code> 方法。该状态的线程位于可运行线程池中，等待被线程调度选中，获取 CPU 的使用权，此时处于就绪状态(ready)。就绪状态的线程在获得 CPU 时间片后变为运行中状态(running)。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">阻塞(BLOCKED)：表示线程阻塞于锁。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">等待(WAITING)：进入该状态的线程需要等待其他线程做出一些特定动作（通知或中断）。1</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">超时等待(TIMED_WAITING)：该状态不同于WAITING，它可以在指定的时间后自行返回。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">终止(TERMINATED)：表示该线程已经执行完毕。</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">状态之间的变迁，如图：</p><div style="width: 86%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/thread.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><h2 id="线程间的共享和协作" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">线程间的共享和协作</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">Java 支持多个线程同时访问一个对象或者访问一个对象里的成员变量，这个就是线程间的共享，共享的资源有，如：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">堆：由于堆是在进程空间中开辟出来的，所以它是理所当然地被共享的，因此new出来的都是共享的</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">全局变量：它是与具体某一方法无关的，所以也与特定线程无关；因此也是共享的</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">静态变量：是共享的</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">文件等公用资源：是共享的</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">独享的资源有：栈和寄存器</p><h3 id="线程的同步(Synchronization)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">线程的同步(Synchronization)</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">但是，线程间的共享存在一些问题，例如(让两个线程操作一个 count 变量进行累加)：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">SharedThread</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> count = <span class="hljs-number" style="line-height: 26px;">0</span>;<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">getCount</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">return</span> count;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">setCount</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> count)</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.count = count;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">addCount</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{<span>        count++;<span>    }<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">CountThread</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span><span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> SharedThread sharedThread;<span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">CountThread</span><span class="hljs-params" style="line-height: 26px;">(SharedThread sharedThread)</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.sharedThread = sharedThread;<span>        }<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; <span class="hljs-number" style="line-height: 26px;">6666</span>; i++) {<span>                sharedThread.addCount();<span>            }<span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> InterruptedException </span>{<span>        SharedThread sharedThread = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> SharedThread();<span>        CountThread countThread = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> CountThread(sharedThread);<span>        CountThread countThread1 = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> CountThread(sharedThread);<span>        countThread.start();<span>        countThread1.start();<span>        Thread.sleep(<span class="hljs-number" style="line-height: 26px;">66</span>);<span>        System.out.println(sharedThread.getCount());<span>        System.out.println(<span class="hljs-number" style="line-height: 26px;">6666</span> <em> <span class="hljs-number" style="line-height: 26px;">2</span>);<span>    }<span>}<span></span></span></span></em></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-number" style="line-height: 26px;">7045</span><span><span class="hljs-number" style="line-height: 26px;">13332</span><span><span>BUILD SUCCESSFUL in <span class="hljs-number" style="line-height: 26px;">247</span>ms<span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行了几次，都是小于 13332(6666  2)，这就是线程间共享的同步问题，解决此问题我们需要使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code>。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">所以，我们把以上代码稍作修改，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">SharedThread</span> </span>{<span><span>    …<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">addCount</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{<span>        count++;<span>    }<span><span>    …<span>}<span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">13332<span>13332<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 324ms<span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们也可以使用，这种方式，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">SharedThread</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> count = <span class="hljs-number" style="line-height: 26px;">0</span>;<span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> Object object = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Object(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 使用 Object 作为锁</span><span><span>    …<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">addCount</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> (object) {<span>            count++;<span>        }<span>    }<span><span>    …<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">这两种方式并没有任何的差别，都是对象锁。</p><h3 id="类锁和对象锁" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">类锁和对象锁</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们来创建一个类 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ClassObjectLock</code> 来演示类锁和对象锁，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">ClassObjectLock</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">ClassLock</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Class Lock is running …”</span>);<span>            lockClass();<span>        }<span>    }<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">ObjectLock</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> ClassObjectLock classObjectLock;<span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">ObjectLock</span><span class="hljs-params" style="line-height: 26px;">(ClassObjectLock classObjectLock)</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.classObjectLock = classObjectLock;<span>        }<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Object Lock is running …”</span>);<span>            classObjectLock.lockObject();<span>        }<span>    }<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">ObjectLock1</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> ClassObjectLock classObjectLock;<span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">ObjectLock1</span><span class="hljs-params" style="line-height: 26px;">(ClassObjectLock classObjectLock)</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.classObjectLock = classObjectLock;<span>        }<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Object Lock1 is running …”</span>);<span>            classObjectLock.lockObject1();<span>        }<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 对象锁</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">lockObject</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        SleepTool.second(<span class="hljs-number" style="line-height: 26px;">2</span>);<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Object Lock use”</span>);<span>        SleepTool.second(<span class="hljs-number" style="line-height: 26px;">2</span>);<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Object Lock end”</span>);<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 对象锁</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">lockObject1</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        SleepTool.second(<span class="hljs-number" style="line-height: 26px;">2</span>);<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Object Lock1 use”</span>);<span>        SleepTool.second(<span class="hljs-number" style="line-height: 26px;">2</span>);<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Object Lock1 end”</span>);<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 类锁，实际是锁类的class对象</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">lockClass</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        SleepTool.second(<span class="hljs-number" style="line-height: 26px;">2</span>);<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Class Lock use”</span>);<span>        SleepTool.second(<span class="hljs-number" style="line-height: 26px;">2</span>);<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“Class Lock end”</span>);<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        ClassObjectLock classObjectLock = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> ClassObjectLock();<span>        ObjectLock objectLock = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> ObjectLock(classObjectLock);<span><span>        ClassObjectLock classObjectLock1 = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> ClassObjectLock();<span>        ObjectLock1 objectLock1 = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> ObjectLock1(classObjectLock1);<span>        objectLock.start();<span>        objectLock1.start();<span><span>        ClassLock classLock = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> ClassLock();<span>        classLock.start();<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Object Lock is running …<span>Object Lock1 is running …<span>Class Lock is running …<span>Object Lock use<span>Object Lock1 use<span>Class Lock use<span>Class Lock end<span>Object Lock end<span>Object Lock1 end<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 4s<span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">由运行结果可知，对象锁和对象锁之前是互不影响的，对象锁和类锁之前也是互不影响的。</p><h3 id="等待和通知(wait、notify)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">等待和通知(wait、notify)</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">等待和通知就是属于线程间的协作，一般有等待方获取锁之后进行条件检查，条件满足，则执行逻辑代码，否则不执行；而通知方获取锁之后进行修改条件，之后通知等待方，实例代码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">WaitNotify</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> String CITY = <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“beijing”</span>;<span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> km;<span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> String site;<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">WaitNotify</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> km, String site)</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.km = km;<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.site = site;<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 改变公里数，并通知</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">changeKm</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.km = <span class="hljs-number" style="line-height: 26px;">66</span>;<span>        notifyAll();<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 改变公站点，并通知</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">changeSite</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.site = <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“guangzhou”</span>;<span>        notifyAll();<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 如果公里数小于 66，就等待</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">waitKm</span><span class="hljs-params" style="line-height: 26px;">()</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> InterruptedException </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">while</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.km &lt; <span class="hljs-number" style="line-height: 26px;">66</span>) {<span>            wait();<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“check km thread: “</span> + Thread.currentThread().getName());<span>        }<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“km is “</span> + <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.km);<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 如果站点是beijing，就等待</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">waitSite</span><span class="hljs-params" style="line-height: 26px;">()</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> InterruptedException </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">while</span> (CITY.equals(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.site)) {<span>            wait();<span>            System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“check site thread: “</span> + Thread.currentThread().getName());<span>        }<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“site is “</span> + <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.site);<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">再新建一个测试类，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Client</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> WaitNotify waitNotify = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> WaitNotify(<span class="hljs-number" style="line-height: 26px;">0</span>, WaitNotify.CITY);<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">CheckKm</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">try</span> {<span>                waitNotify.waitKm();<span>            } <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">catch</span> (InterruptedException e) {<span>                e.printStackTrace();<span>            }<span>        }<span>    }<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">CheckSite</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Thread</span> </span>{<span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">try</span> {<span>                waitNotify.waitSite();<span>            } <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">catch</span> (InterruptedException e) {<span>                e.printStackTrace();<span>            }<span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">throws</span> InterruptedException </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; <span class="hljs-number" style="line-height: 26px;">2</span>; i++) {<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> CheckKm().start();<span>        }<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; <span class="hljs-number" style="line-height: 26px;">2</span>; i++) {<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> CheckSite().start();<span>        }<span><span>        Thread.sleep(<span class="hljs-number" style="line-height: 26px;">1000</span>);<span>        waitNotify.changeKm();<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">check site thread: Thread-3<span>check site thread: Thread-2<span>check km thread: Thread-1<span>km is 66<span>check km thread: Thread-0<span>km is 66<span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">可知 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">notifyAll()</code> 方法会唤醒所有的线程，而 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">notify()</code> 只会唤醒一个线程，且这个线程不一定是我们想唤醒的线程，所有，我们在使用时最好使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">notifyAll()</code> 方法。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">如上代码 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">changeKm()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">waitKm()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">changeSite()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">waitSite()</code> 方法都是对象锁，且锁的是同一个对象，如果有多个线程执行方法，那么他们之间不是有冲突吗？他们之间不会有冲突，因为，调用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">wait()</code> 方法之后，会释放锁，其他方法就可以获取锁。</p><h2 id="显示锁(Lock)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">显示锁(Lock)</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 也被称作内置锁，因为 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 的使用有些局限性，如：无法中断、无法实现尝试获取锁等，所以，Java 给我们提供了 <strong style="font-weight: bold; color: black;">Lock</strong> 也称为显示锁。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Lock</code> 是一个接口，需要我们手动获取或释放锁，<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Lock</code> 拥有 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 所没有的功能，可以被中断 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">lockInterruptibly()</code>，可以尝试获取锁 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">tryLock()</code>等。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Lock</code> 既然是一个接口，必然有实现，<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Lock</code> 的使用有6个，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/lock.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">常用的有 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ReadLock</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">WriteLock</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ReentrantLock</code> 读写锁和可重入锁。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">一般我们会这样使用它，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">LockDemo</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> count = <span class="hljs-number" style="line-height: 26px;">0</span>;<span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> Lock lock = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> ReentrantLock();<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">add</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        lock.lock();<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">try</span> {<span>            count ++;<span>        } <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">finally</span> {<span>            lock.unlock(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 在 finally 里释放锁，确保一定可以执行</span><span>        }<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h3 id="可重入锁(ReentrantLock)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">可重入锁(ReentrantLock)</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">可重入锁就是可以重复获取锁，<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 本身就实现了可重入的功能，所以它也是可重入锁，可重入锁可以防止我们在递归调用时避免自己把自己锁死，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">synchronized</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">add</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>    count ++;<span>    add();<span>}<span></span></span></span></span></code></pre><h3 id="公平锁和非公平锁" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">公平锁和非公平锁</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">公平锁就是在多个线程申请获取锁时，先申请的一定先拿到，非公平锁就是当多个线程去申请获取锁时，后申请的反而先获取到锁。<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 在内部实现上是一个非公平锁，<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ReentrantLock</code> 在默认也是非公平锁，一般非公平锁要比公平锁性能好，因为公平锁需要频繁的挂起和唤醒线程，存在大量的上下文切换。</p><h2 id="ThreadLocal的原理" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">ThreadLocal的原理</span><span class="suffix"></span></h2><h3 id="ThreadLocal和synchronized区别" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">ThreadLocal和synchronized区别</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 都用于解决多线程并发访问，它们的区别在于 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 是利用锁机制，使变量在某一时刻仅被一个线程访问，而 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code> 是为每个线程都提供了变量的副本，使每个线程在某一时刻访问到的并非同一对象，隔离了多个线程对数据的共享。</p><h3 id="ThreadLocal的使用" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">ThreadLocal的使用</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code> 就提供了线程的局部变量，每个线程都可以通过 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">set()</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">get()</code> 来操作这个局部变量，不会和其他线程的局部变量产生冲突，实现了线程的数据隔离。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们来看下不使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code> 的一个案例，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">UseThreadLocal</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> Integer count = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Integer(<span class="hljs-number" style="line-height: 26px;">1</span>);<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 启动 3 个线程</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">StartThread</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        Thread[] threads = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread[<span class="hljs-number" style="line-height: 26px;">3</span>];<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; threads.length; i++) {<span>            threads[i] = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> RunnableThread(i));<span>        }<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; threads.length; i++) {<span>            threads[i].start();<span>        }<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 希望每个线程单独操作自己 count 变量</span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">RunnableThread</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">implements</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Runnable</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> id;<span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">RunnableThread</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> id)</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.id = id;<span>        }<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(Thread.currentThread().getName() + <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“ start”</span>);<span>            count = count + id;<span>            System.out.println(Thread.currentThread().getName() + <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“ count “</span> + count);<span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        UseThreadLocal threadLocal = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> UseThreadLocal();<span>        threadLocal.StartThread();<span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Thread-0 start<span>Thread-0 count 1<span>Thread-2 start<span>Thread-1 start<span>Thread-1 count 4<span>Thread-2 count 3<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 169ms<span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">并不是我们设想的那样，这是因为，count 变量是3个线程所共享的数据导致，我们再来使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code>，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">UseThreadLocal</span> </span>{<span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 使用 ThreadLocal</span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> ThreadLocal&lt;Integer&gt; count = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> ThreadLocal&lt;Integer&gt;(){<span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">protected</span> Integer <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">initialValue</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">return</span> <span class="hljs-number" style="line-height: 26px;">0</span>;<span>        }<span>    };<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 启动 3 个线程</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">StartThread</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        Thread[] threads = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread[<span class="hljs-number" style="line-height: 26px;">3</span>];<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; threads.length; i++) {<span>            threads[i] = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> Thread(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> RunnableThread(i));<span>        }<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> (<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> i = <span class="hljs-number" style="line-height: 26px;">0</span>; i &lt; threads.length; i++) {<span>            threads[i].start();<span>        }<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 希望每个线程单独操作自己 count 变量</span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">RunnableThread</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">implements</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Runnable</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> id;<span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">RunnableThread</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> id)</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.id = id;<span>        }<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            System.out.println(Thread.currentThread().getName() + <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“ start”</span>);<span>            Integer integer = count.get(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 获取 ThreadLocal 里的值</span><span>            integer = integer + id;<span>            count.set(integer); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 如果下次还有使用，需要 set 值</span><span>            System.out.println(Thread.currentThread().getName() + <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“ count “</span> + integer);<span>        }<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        UseThreadLocal threadLocal = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> UseThreadLocal();<span>        threadLocal.StartThread();<span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Thread-0 start<span>Thread-2 start<span>Thread-1 start<span>Thread-0 count 0<span>Thread-2 count 2<span>Thread-1 count 1<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> 530ms<span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">这样就保证了每个线程操作自己的变量的副本，实现了线程的数据隔离。</p><h3 id="ThreadLocal原理解析" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">ThreadLocal原理解析</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们先可以进入 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code> 的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">set()</code> 方法查看源码，如下</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">set</span><span class="hljs-params" style="line-height: 26px;">(T var1)</span> </span>{<span>    Thread var2 = Thread.currentThread(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 获取当前线程</span><span>    ThreadLocal.ThreadLocalMap var3 = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.getMap(var2); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 调用了 getMap() 方法，返回的是 ThreadLocal.ThreadLocalMap</span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> (var3 != <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">null</span>) {<span>        var3.set(<span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>, var1);<span>    } <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">else</span> {<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.createMap(var2, var1);<span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们再进入 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal.ThreadLocalMap</code> 里，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">ThreadLocalMap</span> </span>{<span>  <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> INITIAL_CAPACITY = <span class="hljs-number" style="line-height: 26px;">16</span>;<span>  <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> ThreadLocal.ThreadLocalMap.Entry[] table;  <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 持有 Entry[] 数组</span><span>  <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> size;<span>  <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> threshold;<span><span>  …<span>}<span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">我们再来看看这个数组的定义，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">Entry</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">WeakReference</span>&lt;<span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">ThreadLocal</span>&lt;?&gt;&gt; </span>{<span>    Object value;<span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// Entry 又持有 ThreadLocal 和 Object 成员变量</span><span>    Entry(ThreadLocal&lt;?&gt; var1, Object var2) {<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">super</span>(var1);<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.value = var2;<span>    }<span>}<span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">也就是当我们去 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">new ThreadLocal</code> 它就在当前线程里创建了一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocalMap</code>且这个 Map 里持有多个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Entry[]</code> 型的数组，而每个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Entry</code> 持有成员 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Object</code>，结构如图：</p><div style="width: 66%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/threadlocal1.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">那么，为什么用数组保存 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Entry</code> 呢，因为可能有多个变量需要线程隔离。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">其实，上面我们使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocal</code> 时，用其实例 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">count.get()</code> 就是获取到每个线程独有的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">ThreadLocalMap</code>，然后通过其实例获取到对应的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Entry</code>，就可以获取返回值。</p><h2 id="CAS(Compare And Swap)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">CAS(Compare And Swap)</span><span class="suffix"></span></h2><h3 id="什么是原子操作" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">什么是原子操作</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在编程中，atomic(原子) 动作是一次性完全发生的动作，原子动作不能在中间停止：它要么完全发生，要么根本不发生。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">假设有两个操作 A 和 B (A 和 B 可能都很复杂)，如果从执行 A 的线程来看，当另一个线程执行 B 时，要么将 B 全部执行完成，要么全部执行不完成，那么 A 和 B 对彼此来说是原子的。</p><h3 id="如何实现原子操作" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">如何实现原子操作</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">实现原子操作可以使用锁，锁机制可以满足基本需求，比如：<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">synchronized</code> 所包围的代码就是一个原子操作，Java 也我我们提供了很多原子变量类，如：<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">Atomic</code> 开头的一些类</p><div style="width: 66%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/atomic.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在现代 CUP 里基本都提供了一个 Compare And Swap (CAS)的指令，每个 CAS 操作都包含3个运算符：内存地址、期望值、新值，操作时如果这个地址存放的值等于期望值，则将地址的上的值赋为新增，否则不做任何操作，重新获取值，再来一次，直到成功，如图：</p><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/cas.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><h3 id="CAS原子操作的三大问题" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">CAS原子操作的三大问题</span><span class="suffix" style="display: none;"></span></h3><h4 id="ABA问题" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 18px; margin: 10px auto -1px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 16px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">ABA问题</span><span class="suffix" style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">因为CAS需要在操作值的时候，检查值有没有发生变化，如果没有发生变化则更新，但是如果一个值原来是 A，变成了 B，又变成了 A，那么使用 CAS 进行检查时会发现它的值没有发生变化，但是实际上却变化了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">ABA 问题的解决思路就是使用版本号。在变量前面追加上版本号，每次变量更新的时候把版本号加1，那么A→B→A 就会变成 1A→2B→3A。举个通俗点的例子，你倒了一杯水放桌子上，干了点别的事，然后同事把你水喝了又给你重新倒了一杯水，你回来看水还在，拿起来就喝，如果你不管水中间被人喝过，只关心水还在，这就是ABA问题。</p><h4 id="开销问题" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 18px; margin: 10px auto -1px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 16px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">开销问题</span><span class="suffix" style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">因为，Java 实现 CAS 操作是使用自旋机制，如果 Compare 长时间不相等，会重复执行，给 CPU 带来非常大的开销。</p><h4 id="只能保证一个共享变量的原子操作" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 18px; margin: 10px auto -1px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 16px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">只能保证一个共享变量的原子操作</span><span class="suffix" style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">因为，CAS 是对地址上的值进行操作，因此它只能操作一个变量，如果我们需要同时操作多个变量 CAS 就无法保证操作的原子性。</p><h3 id="原子操作类的使用" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">原子操作类的使用</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">AtomicInteger</code> 的使用，如下:</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">AtomicInt</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> AtomicInteger atomicInteger = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> AtomicInteger(<span class="hljs-number" style="line-height: 26px;">6</span>);<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        atomicInteger.getAndDecrement(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 自增1，返回之前的值</span><span>        System.out.println(atomicInteger);<span>        atomicInteger.incrementAndGet(); <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 自增1，返回新增</span><span>        System.out.println(atomicInteger);<span>        System.out.println(atomicInteger.addAndGet(<span class="hljs-number" style="line-height: 26px;">6</span>));<span>        System.out.println(atomicInteger.getAndAdd(<span class="hljs-number" style="line-height: 26px;">6</span>));<span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">AtomicReference</code> 的使用，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">UseAtomicReference</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> AtomicReference&lt;UserInfo&gt; reference; <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 原子更新引用类型</span><span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        UserInfo user = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> UserInfo(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“lsy”</span>, <span class="hljs-number" style="line-height: 26px;">66</span>);<span>        reference = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> AtomicReference(user);<span>        UserInfo updateUser = <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">new</span> UserInfo(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“per”</span>,<span class="hljs-number" style="line-height: 26px;">36</span>);<span>        reference.compareAndSet(user,updateUser);<span><span>        System.out.println(reference.get());<span>        System.out.println(user);<span>    }<span><span>    <span class="hljs-comment" style="color: #75715e; line-height: 26px;">//定义一个实体类</span><span>    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">UserInfo</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">volatile</span> String name;<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> age;<span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">UserInfo</span><span class="hljs-params" style="line-height: 26px;">(String name, <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> age)</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.name = name;<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.age = age;<span>        }<span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> String <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">getName</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">return</span> name;<span>        }<span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">int</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">getAge</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">return</span> age;<span>        }<span><span>        <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>        <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> String <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">toString</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>            <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">return</span> <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“UserInfo{“</span> +<span>                    <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“name=’”</span> + name + <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">‘\’’</span> +<span>                    <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“, age=”</span> + age +<span>                    <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">‘}’</span>;<span>        }<span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">UserInfo{name=<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">‘per’</span>, age=<span class="hljs-number" style="line-height: 26px;">36</span>}<span>UserInfo{name=<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">‘lsy’</span>, age=<span class="hljs-number" style="line-height: 26px;">66</span>}<span><span>BUILD SUCCESSFUL in <span class="hljs-number" style="line-height: 26px;">552</span>ms<span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">Jdk中相关原子操作类有如下：</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">更新基本类型类：AtomicBoolean，AtomicInteger，AtomicLong更新数组类：AtomicIntegerArray，AtomicLongArray，AtomicReferenceArray更新引用类型：AtomicReference，AtomicMarkableReference，AtomicStampedReference</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">大部分用法都是类似的，在此，就不一一演示，感兴趣的小伙伴可以自行尝试。</p><h2 id="阻塞队列" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">阻塞队列</span><span class="suffix"></span></h2><h3 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">队列</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">队列(queue)是一种采用先进先出(FIFO)策略的抽象数据结构，即最先进队列的数据元素，同样要最先出队列。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在队列中插入一个队列元素称为入队，从队列中删除一个队列元素称为出队，因为队列只允许在一端插入，在另一端删除，所以只有最早进入队列的元素才能最先从队列中删除，故队列又称为先进先出(FIFO—first in first out)线性表。</p><h3 id="什么是阻塞队列" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">什么是阻塞队列</span><span class="suffix" style="display: none;"></span></h3><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">支持阻塞的插入方法：当队列满时，队列会阻塞插入元素的线程，直到队列空余。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">支持阻塞的移除方法：当队列为空时，获取元素的线程会等待队列为非空。</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在并发编程中使用生产者和消费者模式可以解决大多数并发问题，该模式通过平衡生产线程和消费线程的工作能力来提高程序处理数据的速度。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在线程世界里，生产者就是生产数据的线程，消费者就是消费数据的线程。在多线程开发中，如果生产者处理速度很快，而消费者处理速度很慢，那么生产者就必须等待消费者处理完，才能继续生产数据。同样的道理，如果消费者的处理能力大于生产者，那么消费者就必须等待生产者。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">为了解决这种生产消费能力不均衡的问题，便有了生产者和消费者模式。生产者和消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通信，而是通过阻塞队列来进行通信，所以生产者生产完数据之后不用等待消费者处理，直接扔给阻塞队列，消费者不找生产者要数据，而是直接从阻塞队列里取，阻塞队列就相当于一个缓冲区，平衡了生产者和消费者的处理能力。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">在 Java 中阻塞队列有一个专门的接口 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">BlockingQueue</code>，如图</p><div style="width: 66%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/BlockingQueue.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">它定义了一些方法，但是这些方法不是所有的都是阻塞的，如：<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">add()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">remove()</code> 方法都是非阻塞的，<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">put()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">take()</code> 方法是阻塞的。</p><h3 id="常用的阻塞队列" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">常用的阻塞队列</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">以下的阻塞队列都实现了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">BlockingQueue</code> 接口，也都是线程安全的，如：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">ArrayBlockingQueue：一个由数组结构组成的有界阻塞队列。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">LinkedBlockingQueue：一个由链表结构组成的有界阻塞队列。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">PriorityBlockingQueue：一个支持优先级排序的无界阻塞队列。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">DelayQueue：一个使用优先级队列实现的无界阻塞队列。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">SynchronousQueue：一个不存储元素的阻塞队列。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">LinkedTransferQueue：一个由链表结构组成的无界阻塞队列。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">LinkedBlockingDeque：一个由链表结构组成的双向阻塞队列。</section></li></ul><h3 id="有界无界阻塞队列" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">有界无界阻塞队列</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">有界队列就是长度有限，满了以后生产者会阻塞，无界队列就是里面能放无数的东西而不会因为队列长度限制被阻塞，当然空间限制来源于系统资源的限制，如果处理不及时，导致队列越来越大。所以，在我们实际开发中尽量使用有界阻塞队列。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">无界也会阻塞，因为阻塞不仅仅体现在生产者放入元素时会阻塞，消费者拿取元素时，如果没有元素，同样也会阻塞。</p><h2 id="AQS(AbstractQueuedSynchronizer)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 22px; margin: 10px auto; height: 40px; background-color: rgb(251, 251, 251); border-bottom: 1px solid rgb(246, 246, 246); overflow: hidden; box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-left: -10px; display: inline-block; width: auto; height: 40px; background-color: rgb(33, 33, 34); border-bottom-right-radius: 100px; color: rgb(255, 255, 255); padding-right: 30px; padding-left: 30px; line-height: 40px; font-size: 20px;">AQS(AbstractQueuedSynchronizer)</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">队列同步器 AbstractQueuedSynchronizer(简称同步器或AQS)，是用来构建锁或者其他同步组件的基础框架，它使用了一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">int</code> 成员变量表示同步状态，通过内置的 FIFO 队列来完成资源获取线程的排队工作。</p><h3 id="AQS使用方式" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">AQS使用方式</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">AQS 的主要使用方式是继承，子类通过继承 AQS 并实现它的抽象方法来管理同步状态，在 AQS 里由一个<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">int</code> 型的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">state</code> 来代表这个状态，在抽象方法的实现过程中对同步状态进行更改，这时就需要使用同步器提供的3个方法 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">getState()</code>、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">setState(int newState)</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">compareAndSetState(int expect,int update)</code> 来进行操作，因为它们能够保证状态的改变是安全的。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">AQS 是实现锁的关键，锁是面向使用者，它定义了使用者与锁交互的接口，隐藏了实现细节；AQS 面向的是锁的实现者，它简化了锁的实现方式，屏蔽了同步状态管理、线程的排队、等待、唤醒等底层操作。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">实现者需要继承 AQS 并重写指定方法，然后将 AQS 组合在自定义同步组件的实现中，并调用 AQS 提供的模板方法，而这些模板方法将会调用使用者重写的方法。</p><h3 id="模板方法设计模式" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">模板方法设计模式</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">AQS 的设计师基于模板方法设计模式，模板方法设计模式是定义一个操作的算法的架子，而将一些步骤的实现延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">例如，我们想要做蛋糕，我们需要一个模型，每个人想做什么蛋糕，由他自己实现，代码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 蛋糕的模型，定义好了做蛋糕的步骤方法</span><span><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">abstract</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">AbstractCake</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">abstract</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">mould</span><span class="hljs-params" style="line-height: 26px;">()</span></span>; <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 制作形状</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">abstract</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">butter</span><span class="hljs-params" style="line-height: 26px;">()</span></span>; <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 涂抹奶油</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">abstract</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">toast</span><span class="hljs-params" style="line-height: 26px;">()</span></span>; <span class="hljs-comment" style="color: #75715e; line-height: 26px;">// 烤面包</span><span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">making</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.mould();<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.butter();<span>        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">this</span>.toast();<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">子类去继承它，重写这些方法，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px;"><code class="hljs" style="overflow-x: auto; padding: 16px; background: #272822; color: #ddd; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">class</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">CheeseCake</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">extends</span> <span class="hljs-title" style="font-weight: bold; color: white; line-height: 26px;">AbstractCake</span> </span>{<span><span>    <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">mould</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“芝士蛋糕制作形状 …”</span>);<span>    }<span><span>    <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">butter</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“芝士蛋糕涂抹奶油 …”</span>);<span>    }<span><span>    <span class="hljs-meta" style="color: #75715e; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">void</span> <span class="hljs-title" style="color: #a6e22e; font-weight: bold; line-height: 26px;">toast</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        System.out.println(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">“芝士蛋糕烤面包 …”</span>);<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h3 id="CLH队列锁" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; padding: 0px; font-weight: bold; color: black; font-size: 20px; margin: 20px auto 5px; border-top: 1px solid rgb(221, 221, 221); box-sizing: border-box;"><span class="prefix" style="display: none;"></span><span class="content" style="margin-top: -1px; padding-top: 6px; padding-right: 5px; padding-left: 5px; font-size: 18px; border-top: 2px solid rgb(33, 33, 34); display: inline-block; line-height: 1.1;">CLH队列锁</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">CLH 队列锁也是一种基于链表的可扩展、高性能、公平的自旋锁，线程仅仅在本地变量上自旋，不断轮询前驱的状态，发现前驱释放了锁就结束自旋。</p><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">当一个线程需要获取锁时：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">创建一个 QNode，将其中的 locked 设为 true 表示获取锁，如图：</section></li></ul><div style="width: 30%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/qnode.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">myPred 表示前驱节点的引用。</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">线程 A 对 tail 域调用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: rgb(239, 112, 96);">getAndSet</code> 方法，使自己成为队列的尾部，同时获取一个指向前驱节点的引用 myPred，如图：</section></li></ul><div style="width: 46%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/qnode1.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">线程 B 需要获得锁，于是，也需要按照相同的流程，如图：</p><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/qnode2.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">线程就在前驱的节点的 locked 字段上自旋，直到前驱节点释放锁</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; font-size: 15px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;">当一个线程需要释放锁时，会将当前节点的 locked 域设置为 false，同时回收前驱节点，如图</section></li></ul><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/thread-concurrent/qnode3.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="padding-top: 8px; padding-bottom: 8px; margin: 0; color: black; box-sizing: border-box; margin-bottom: 16px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 15px; text-align: start; white-space: normal; text-size-adjust: auto; line-height: 1.75em;">前驱节点释放锁后，线程 A 的 myPred 所指向的前驱节点的 locked 字段变为 false，线程 A 就可以获取锁。AQS 就是 CLH 队列锁的一种变体实现。</p></section>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/thread-concurrent.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Android coder 需要理解的注解、反射和动态代理</title>
      <link>https://h.lishaoy.net/annotations-reflect.html</link>
      <guid>https://h.lishaoy.net/annotations-reflect.html</guid>
      <pubDate>Mon, 27 Jul 2020 22:33:01 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/annotations-reflect/annotations-reflect-proxy2.png&quot; class=&quot;full-image&quot; alt=&quot;annotations reflect proxy&quot; title=&quot;annotations reflect proxy&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;section id=&quot;nice&quot; data-tool=&quot;mdnice编辑器&quot; data-website=&quot;https://www.mdnice.com&quot; style=&quot;padding: 0 10px; line-height: 1.6; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#39;PingFang SC&#39;, Cambria, Cochin, Georgia, Times, &#39;Times New Roman&#39;, serif; font-size: 15px; letter-spacing: 0.05em; color: #595959;&quot;&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;&quot;&gt;注解我们经常使用它，很多框架也提供了很多注解给我们使用，如 &lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;&quot;&gt;ARouter&lt;/code&gt; 的 &lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;&quot;&gt;@Route(path = “/test/activity”)&lt;/code&gt; 、&lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;&quot;&gt;butterknife&lt;/code&gt; 的 &lt;code style=&quot;font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;&quot;&gt;@BindView(R.id.user) EditText username;&lt;/code&gt; 等，但是，你有没有自定义过注解，写过自己的注解处理器呢？反射听起来很高大上，但是实际上你真的了解他之后，只是一些API的调用而已；动态代理其实只是在静态代理(代理模式)基础上使用了反射技术；本篇文章将带领大家对注解、反射及动态代理有更清晰的认知。&lt;/p&gt;
&lt;hr data-tool=&quot;mdnice编辑器&quot; style=&quot;height: 1px; margin-top: 10px; margin-bottom: 10px; border-top: 1px solid black; border: 1px solid #35b378; margin: 1.5em auto;background: white;&quot;&gt;&lt;/section&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/annotations-reflect/annotations-reflect-proxy2.png" class="full-image" alt="annotations reflect proxy" title="annotations reflect proxy"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0 10px; line-height: 1.6; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 15px; letter-spacing: 0.05em; color: #595959;"><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">注解我们经常使用它，很多框架也提供了很多注解给我们使用，如 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ARouter</code> 的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@Route(path = “/test/activity”)</code> 、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">butterknife</code> 的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@BindView(R.id.user) EditText username;</code> 等，但是，你有没有自定义过注解，写过自己的注解处理器呢？反射听起来很高大上，但是实际上你真的了解他之后，只是一些API的调用而已；动态代理其实只是在静态代理(代理模式)基础上使用了反射技术；本篇文章将带领大家对注解、反射及动态代理有更清晰的认知。</p><hr data-tool="mdnice编辑器" style="height: 1px; margin-top: 10px; margin-bottom: 10px; border-top: 1px solid black; border: 1px solid #35b378; margin: 1.5em auto;background: white;"></section><a id="more"></a><section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0 10px; line-height: 1.6; word-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif; font-size: 15px; letter-spacing: 0.05em; color: #595959;"><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">本篇文章的示例代码放在 <a href="https://github.com/persilee/android_practice" style="text-decoration: none; word-wrap: break-word; font-weight: bold; color: #35b378; border-bottom: 1px solid #35b378;" target="_blank" rel="noopener">Github</a> 上，所有知识点，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations-reflect-proxy.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><h2 data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; bmin-height: 32px; line-height: 32px; border-bottom: solid 1px #000000; color: #35b378; display: inline-block; border-bottom-width: 0px; border-bottom-style: solid; border-color: #35b378; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; font-size: 23px; margin: 1em 0 0rem 0; padding: 0.5em 0; text-align: leftt; font-weight: bold;"><span class="prefix" style="display: none;"></span><span class="content">注解</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">注解(Annotations)，元数据的一种形式，提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">注解有多种用途，例如：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">为编译器提供信息：编译器可以使用注解来检查错误或抑制警告</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">编译或部署时处理：可以生成代码、XML、文件等</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">运行时处理：注解可以在运行时检查</section></li></ul><h3 id="注解的格式" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">注解的格式</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">注解的格式如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span><span><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MyClass</span> </span>{ … }<span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">注解以 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@</code> 开头后面跟上内容，注解可以包含元素，例如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span>(id=<span class="hljs-number" style="color: #986801; line-height: 26px;">666</span>, value = <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>)<span><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MyClass</span> </span>{ … }<span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">如果，只有一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">value</code> 元素，则可以省略该名称，如果，没有元素，则可以省略括号，例如</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>) <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 只有一个 value 元素</span><span><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MyClass</span> </span>{ … }<span><span><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span> <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 没有元素</span><span><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MyClass</span> </span>{ … }<span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">如果，注解有相同的类型，则是重复注解，如</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>)<span><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“zimu”</span>)<span><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MyClass</span> </span>{ … }<span></span></span></span></code></pre><h3 id="注解声明" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">注解声明</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">注解的定义类似于接口的定义，在关键字 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">interface</code> 前加上 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@</code>，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@interface</span> Persilee {<span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">id</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<span>    <span class="hljs-function" style="line-height: 26px;">String <span class="hljs-title" style="color: #4078f2; line-height: 26px;">value</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<span>}<span></span></span></span></span></code></pre><h3 id="注解类型" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">注解类型</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">int id()</code> 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">String value()</code> 是注解类型(annotation type)，它们也可以定义可选的默认值，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@interface</span> Persilee {<span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">id</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<span>    <span class="hljs-function" style="line-height: 26px;">String <span class="hljs-title" style="color: #4078f2; line-height: 26px;">value</span><span class="hljs-params" style="line-height: 26px;">()</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">default</span> “lsy”</span>;<span>}<span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">在使用注解时，如果定义的注解的注解类型没有默认值，则必须进行赋值，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span>(id = <span class="hljs-number" style="color: #986801; line-height: 26px;">666</span>) <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// id 必须要赋值，如，@Persilee 会提示 id 必须赋值</span><span><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MyClass</span> </span>{ … }<span></span></span></code></pre><h3 id="元注解" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">元注解</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">在注解上面的注解称为元注解(meta-annotations)，如</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Target</span>({ElementType.TYPE, ElementType.METHOD})<span><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Retention</span>(RetentionPolicy.SOURCE)<span><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@interface</span> Persilee {<span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">id</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<span>    <span class="hljs-function" style="line-height: 26px;">String <span class="hljs-title" style="color: #4078f2; line-height: 26px;">value</span><span class="hljs-params" style="line-height: 26px;">()</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">default</span> “lsy”</span>;<span>}<span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">java.lang.annotation</code> 中定义了几种元注解类型(常使用的是 @Retention、@Target)，如</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><strong style="font-weight: bold; color: #35b378;">@Retention</strong> 指定注解的存储方式，我们由 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">RetentionPolicy.java</code> (是一个枚举)可知，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">enum</span> RetentionPolicy {<span>    SOURCE, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 标记的注解仅保留在源级别中，并被编译器忽略。</span><span>    CLASS, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 标记的注解在编译时由编译器保留，但 Java 虚拟机(JVM)会忽略。</span><span>    RUNTIME <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 标记的注解由 JVM 保留，因此运行时环境可以使用它。</span><span>}<span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><strong style="font-weight: bold; color: #35b378;">@Target</strong> 指定注解可以使用的范围，我们由 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ElementType.java</code> (是一个枚举)可知使用范围，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">enum</span> ElementType {<span>    TYPE, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 类</span><span>    FIELD, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 字段或属性</span><span>    METHOD, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 方法</span><span>    PARAMETER, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 参数</span><span>    CONSTRUCTOR, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 构造方法</span><span>    LOCAL_VARIABLE, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 局部变量</span><span>    ANNOTATION_TYPE, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 也可以使用在注解上</span><span>    PACKAGE, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 包</span><span>    TYPE_PARAMETER, <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 类型参数</span><span>    TYPE_USE <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 任何类型</span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">对于 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">TYPE_PARAMETER</code> (类型参数) 、 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">TYPE_USE</code> (任何类型名称) 可能不是很好理解，如果把 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Target</code> 设置成 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@Target({ElementType.TYPE_PARAMETER})</code>，表示可以使用在泛型(上篇文章有介绍过<a href="https://h.lishaoy.net/generics.html" style="text-decoration: none; word-wrap: break-word; font-weight: bold; color: #35b378; border-bottom: 1px solid #35b378;">泛型</a>)的类型参数上，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">TypeParameterClass</span>&lt;@<span class="hljs-title" style="color: #c18401; line-height: 26px;">Persilee</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">T</span>&gt; </span>{<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> &lt;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span> T&gt; <span class="hljs-function" style="line-height: 26px;">T <span class="hljs-title" style="color: #4078f2; line-height: 26px;">foo</span><span class="hljs-params" style="line-height: 26px;">(T t)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<span>    }<span>}<span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">如果把 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Target</code> 设置成 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@Target({ElementType.TYPE_USE})</code>，表示可以使用在任何类型上，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">TypeParameterClass&lt;<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span> String&gt; typeParameterClass = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> TypeParameterClass&lt;&gt;();<span><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span> String text = (<span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span> String)<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> Object();<span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><strong style="font-weight: bold; color: #35b378;">@Documented</strong> 注解表示使用了指定的注解，将使用 Javadoc 工具记录这些元素。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><strong style="font-weight: bold; color: #35b378;">@Inherited</strong> 注解表示注解类型可以从超类继承。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><strong style="font-weight: bold; color: #35b378;">@Repeatable</strong> 注解表明标记的注解可以多次应用于同一声明或类型使用。</p><h3 id="注解应用场景" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">注解应用场景</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">根据 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@Retention</code> 元注解定义的存储方式，注解一般可以使用在以下3种场景中，如：</p><section class="table-container" data-tool="mdnice编辑器" style="overflow-x: auto;"><table style="display: table; text-align: left;"><thead><tr style="border: 0; border-top: 1px solid #ccc; background-color: white;"><th style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; font-weight: bold; background-color: #f0f0f0; min-width: 85px; text-align: left;">级别</th><th style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; font-weight: bold; background-color: #f0f0f0; min-width: 85px; text-align: left;">技术</th><th style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; font-weight: bold; background-color: #f0f0f0; min-width: 85px; text-align: left;">说明</th></tr></thead><tbody style="border: 0;"><tr style="border: 0; border-top: 1px solid #ccc; background-color: white;"><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">源码</td><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">APT</td><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">在编译期能获取注解与注解声明的类和类中所有成员信息，一般用于生成额外的辅助类。</td></tr><tr style="border: 0; border-top: 1px solid #ccc; background-color: #F8F8F8;"><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">字节码 </td><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">字节码增强 </td><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">在编译出Class后，通过修改Class数据以实现修改代码逻辑目的，对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。</td></tr><tr style="border: 0; border-top: 1px solid #ccc; background-color: white;"><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">运行时</td><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">反射</td><td style="font-size: 16px; border: 1px solid #ccc; padding: 5px 10px; min-width: 85px; text-align: left;">在程序运行时，通过反射技术动态获取注解与其元素，从而完成不同的逻辑判断。</td></tr></tbody></table></section><h3 id="小案例(使用注解实现语法检查)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">小案例(使用注解实现语法检查)</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们定义一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">weekDay</code> 字段，类型是 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">WeekDay</code> 枚举类型，方便我们设置枚举中指定的值，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">WeekDayDemo</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> WeekDay weekDay;<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">enum</span> WeekDay {<span>        SATURDAY,SUNDAY<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> WeekDay <span class="hljs-title" style="color: #4078f2; line-height: 26px;">getWeekDay</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> weekDay;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">setWeekDay</span><span class="hljs-params" style="line-height: 26px;">(WeekDay weekDay)</span> </span>{<span>        WeekDayDemo.weekDay = weekDay;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        setWeekDay(WeekDay.SATURDAY);<span>        System.out.println(getWeekDay());<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">众所周知，在 Java 中枚举的实质是特殊的静态成员变量，在运行时候，所有的枚举会作为单例加载到内存中，非常消耗内存，那么，有没有什么优化的方案呢，在此，我们使用注解来取代枚举。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们使用常量和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@intDef</code> (语法检查)元注解去代替枚举，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">IntdefDemo</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> SATURDAY = <span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> SUNDAY = <span class="hljs-number" style="color: #986801; line-height: 26px;">1</span>;<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> weekDay;<span><span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@IntDef</span>({SATURDAY, SUNDAY})<span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Target</span>({ElementType.FIELD, ElementType.PARAMETER})<span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Retention</span>(RetentionPolicy.SOURCE)<span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@interface</span> WeekDay { <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//自定义一个 WeekDay 注解</span><span><span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">setWeekDay</span><span class="hljs-params" style="line-height: 26px;">(@WeekDay <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> weekDay)</span> </span>{ <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 使用 WeekDay 注解限制参数类型</span><span>        IntdefDemo.weekDay = weekDay;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        setWeekDay(SATURDAY); <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 只能 传入 SATURDAY, SUNDAY</span><span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h3 id="APT注解处理器" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">APT注解处理器</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">APT(Annotation Processor Tools) 注解处理器，用于处理注解，编写好的 Java 文件，需要经过 Javac 的编译，编译为虚拟机能够加载的字节码(Class)文件，注解处理器是 Javac 自带的一个工具，用来在编译时期处理注解信息。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">上文中我们已自定义好了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@Persilee</code> 注解，下面我们来编写一个简单的注解处理器来处理 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@Persilee</code> 注解，我们可以新建一个 Java 的 Module，创建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">PersileeProcessor</code> 的类，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@SupportedAnnotationTypes</span>(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.anreprdemo.Persilee”</span>)  <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//指定要处理的注解</span><span><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">PersileeProcessor</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">AbstractProcessor</span> </span>{<span><span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">boolean</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">process</span><span class="hljs-params" style="line-height: 26px;">(Set&lt;? extends TypeElement&gt; set, RoundEnvironment roundEnvironment)</span> </span>{<span>        Messager messager = processingEnv.getMessager(); <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//</span><span>        messager.printMessage(Diagnostic.Kind.NOTE, <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“APT working …”</span>);<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span> (TypeElement typeElement: set) {<span>            messager.printMessage(Diagnostic.Kind.NOTE,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“===&gt;”</span> + typeElement.getQualifiedName());<span>            Set&lt;? extends Element&gt; elements = roundEnvironment.getElementsAnnotatedWith(typeElement);<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span> (Element element: elements) {<span>                messager.printMessage(Diagnostic.Kind.NOTE,<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“===&gt;”</span> + element.getSimpleName());<span>            }<span>        }<span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">false</span>;<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">然后，在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">main</code> 目录下新建 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">resources</code> 目录，如图：</p><div style="width: 86%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations1.png" alt="no-shadow" title="annotation" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">这个目录结构是规定死的，必须这样写，然后在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">javax.annotation.processing.Processor</code> 文件里注册需要处理的注解处理器，如</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">net.lishaoy.aptlib.PersileeProcessor<span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">最后，在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">app</code> 的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">build.gradle</code> 文件引入模块，如</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">dependencies {<span>  …<span><span>  <span class="hljs-function" style="line-height: 26px;">annotationProcessor <span class="hljs-title" style="color: #4078f2; line-height: 26px;">project</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">‘:aptlib’</span>)</span><span>}<span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">在你 Build 工程时候，会在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Task :app:compileDebugJavaWithJavac</code> 任务打印我们在注解处理程序的日志信息，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">注: APT working …<span>注: ===&gt;net.lishaoy.anreprdemo.Persilee<span>注: ===&gt;MainActivity<span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">因为，我们只在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">MainActivity</code> 中使用了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@Persilee</code> 注解，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Persilee</span>(id = <span class="hljs-number" style="color: #986801; line-height: 26px;">666</span>, value = <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>)<span><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MainActivity</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span> </span>{<span><span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle savedInstanceState)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<span><span>        setContentView(R.layout.activity_main);<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></code></pre><h2 id="反射" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; bmin-height: 32px; line-height: 32px; border-bottom: solid 1px #000000; color: #35b378; display: inline-block; border-bottom-width: 0px; border-bottom-style: solid; border-color: #35b378; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; font-size: 23px; margin: 1em 0 0rem 0; padding: 0.5em 0; text-align: leftt; font-weight: bold;"><span class="prefix" style="display: none;"></span><span class="content">反射</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">一般情况下，我们使用某个类时必定知道它是什么类，用来做什么的。于是我们直接对这个类进行实例化，之后使用这个类对象进行操作。</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Cook cook = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> Cook(); <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 实例化一个对象，标准用法</span><span>cook.cookService(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“🍅”</span>);<span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">反射是一开始并不知道初始化的类对象是什么，也不能使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">new</code> 关键字来创建对象，反射是在运行的时才知道要操作的类是什么，并且可以在运行时获取类的完整构造，调用对应的方法。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">Java 反射机制主要提供了以下功能:</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">在运行时构造任意一个类的对象</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">在运行时获取或修改任意一个类所具有的成员变量和方法</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">在运行时调用任意一个对象的方法(属性)</section></li></ul><h3 id="Class类" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">Class类</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">Class是一个类，封装了当前对象所对应的类的信息，我们写的每一个类都可以看成一个对象，是 java.lang.Class 类的对象，Class是用来描述类的类。</p><h3 id="获得Class对象" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">获得Class对象</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">Class对象的获取有3种方式，如下：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">通过类名获取 类名.class</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">通过对象获取 对象名.getClass()</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">通过全类名获取 Class.forName(全类名)</section></li></ul><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Cook cook = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> Cook();<span>Class cookClass = Cook.class;<span>Class cookClass1 = cook.getClass();<span>Class cookClass2 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.reflectdemo.Cook”</span>);<span></span></span></span></span></code></pre><h3 id="创建实例" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">创建实例</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们可以通过反射来生成对象的实例，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">Class cookClass = Cook.class;<span>Cook cook1 = (Cook) cookClass.newInstance();<span></span></span></code></pre><h3 id="获取构造器" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">获取构造器</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">获取构造器的方法有，如下：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Constructor getConstructor(Class[] params)：获得使用特殊的参数类型的public构造函数(包括父类)</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Constructor[] getConstructors()：获得类的所有公共构造函数</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Constructor getDeclaredConstructor(Class[] params)：获得使用特定参数类型的构造函数(包括私有)</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Constructor[] getDeclaredConstructors()：获得类的所有构造函数(与接入级别无关)</section></li></ul><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们来新建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Person</code> ，以便我们的演示，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">Person</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> String name;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> age;<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">Person</span><span class="hljs-params" style="line-height: 26px;">(String name, <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> age)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name = name;<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.age = age;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">Person</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>();<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> String <span class="hljs-title" style="color: #4078f2; line-height: 26px;">getName</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“get name: “</span> + name);<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> name;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">setName</span><span class="hljs-params" style="line-height: 26px;">(String name)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name = name;<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“set name: “</span> + <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.name);<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">getAge</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“get age: “</span> + age);<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> age;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">setAge</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> age)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.age = age;<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“set age: “</span> + <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.age);<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">privateMethod</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“the private method!”</span>);<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">很常规的一个类，里面有私有的属性和方法。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">下面，我们新建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">GetConstructor</code> 的类来演示，获取构造器方法如何使用，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">GetConstructor</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span><span>            ClassNotFoundException,<span>            NoSuchMethodException,<span>            IllegalAccessException,<span>            InvocationTargetException,<span>            InstantiationException </span>{<span><span>        String className = <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.reflectdemo.entity.Person”</span>;<span>        Class&lt;Person&gt; personClass = (Class&lt;Person&gt;) Class.forName(className);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//获取全部的constructor对象</span><span>        Constructor&lt;?&gt;[] constructors = personClass.getConstructors();<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span> (Constructor&lt;?&gt; constructor: constructors) {<span>            System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取全部的constructor对象: “</span> + constructor);<span>        }<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//获取某一个constructor对象</span><span>        Constructor&lt;Person&gt; constructor = personClass.getConstructor(String.class, <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>.class);<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取某一个constructor对象: “</span> + constructor);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//调用构造器的 newInstance() 方法创建对象</span><span>        Person person = constructor.newInstance(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“lsy”</span>, <span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<span>        System.out.println(person.getName() + <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“, “</span> + person.getAge() );<span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">输出结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">获取全部的constructor对象: public net.lishaoy.reflectdemo.entity.Person(java.lang.String,int)<span>获取全部的constructor对象: public net.lishaoy.reflectdemo.entity.Person()<span>获取某一个constructor对象: public net.lishaoy.reflectdemo.entity.Person(java.lang.String,int)<span>lsy, 66<span></span></span></span></span></code></pre><h3 id="获取方法" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">获取方法</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">获取方法的方法有，如下：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Method getMethod(String name, Class[] params)：使用特定的参数类型，获得命名的公共方法</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Method[] getMethods()：获得类的所有公共方法</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Method getDeclaredMethod(String name, Class[] params)：使用特写的参数类型，获得类声明的命名的方法</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Method[] getDeclaredMethods()：获得类声明的所有方法</section></li></ul><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们新创建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">GetMethod</code> 来演示如何来获取和调用方法，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">GetMethod</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span><span>            ClassNotFoundException,<span>            NoSuchMethodException,<span>            IllegalAccessException,<span>            InstantiationException,<span>            InvocationTargetException </span>{<span><span>        Class&lt;?&gt; aClass = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.reflectdemo.entity.Person”</span>);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//获取所有的public方法(包含从父类继承的方法)</span><span>        Method[] methods = aClass.getMethods();<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span> (Method method: methods) {<span>            System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取所有public方法： “</span> + method.getName() + <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“()”</span>);<span>        }<span><span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“===========================”</span>);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//获取所有方法(不包含父类方法)</span><span>        methods = aClass.getDeclaredMethods();<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span> (Method method: methods) {<span>            System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取所有方法: “</span> + method.getName() + <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“()”</span>);<span>        }<span><span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“===========================”</span>);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//获取指定的方法</span><span>        Method method = aClass.getDeclaredMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“setAge”</span>, <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span>.class);<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取指定的方法:”</span> + method);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//调用方法</span><span>        Object instance = aClass.newInstance();<span>        method.invoke(instance, <span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//调用私有方法</span><span>        method = aClass.getDeclaredMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“privateMethod”</span>);<span>        method.setAccessible(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">true</span>); <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 需要调用此方法且设置为 true</span><span>        method.invoke(instance);<span><span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">获取所有public方法： getName()<span>获取所有public方法： setName()<span>获取所有public方法： setAge()<span>获取所有public方法： getAge()<span>获取所有public方法： <span class="hljs-built_in" style="color: #c18401; line-height: 26px;">wait</span>()<span>获取所有public方法： <span class="hljs-built_in" style="color: #c18401; line-height: 26px;">wait</span>()<span>获取所有public方法： <span class="hljs-built_in" style="color: #c18401; line-height: 26px;">wait</span>()<span>获取所有public方法： equals()<span>获取所有public方法： toString()<span>获取所有public方法： hashCode()<span>获取所有public方法： getClass()<span>获取所有public方法： notify()<span>获取所有public方法： notifyAll()<span>===========================<span>获取所有方法: getName()<span>获取所有方法: setName()<span>获取所有方法: setAge()<span>获取所有方法: privateMethod()<span>获取所有方法: getAge()<span>===========================<span>获取指定的方法:public void net.lishaoy.reflectdemo.entity.Person.setAge(int)<span><span class="hljs-built_in" style="color: #c18401; line-height: 26px;">set</span> age: 66<span>the private method!<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span> 395ms<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h3 id="获取成员变量" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">获取成员变量</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">获取成员变量的方法有，如下：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Field getField(String name)：获得命名的公共字段</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Field[] getFields()：获得类的所有公共字段</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Field getDeclaredField(String name)：获得类声明的命名的字段</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">Field[] getDeclaredFields()：获得类声明的所有字段</section></li></ul><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们再来新建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">GetField</code> 的类来演示如何获取成员变量，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">GetField</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span><span>            ClassNotFoundException,<span>            NoSuchFieldException,<span>            IllegalAccessException,<span>            InstantiationException </span>{<span><span>        Class&lt;?&gt; aClass = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.reflectdemo.entity.Person”</span>);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取所有字段(不包含父类字段)</span><span>        Field[] fields = aClass.getDeclaredFields();<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span> (Field field: fields) {<span>            System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取所有字段: “</span> + field.getName());<span>        }<span><span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“================”</span>);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取指定字段</span><span>        Field name = aClass.getDeclaredField(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“name”</span>);<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取指定字段: “</span> + name.getName());<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 设置指定字段的值</span><span>        Object instance = aClass.newInstance();<span>        name.set(instance, <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“per”</span>);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取指定字段的值</span><span>        Object o = name.get(instance);<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取指定字段的值: “</span> + o);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 设置和获取私有字段的值</span><span>        Field age = aClass.getDeclaredField(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“age”</span>);<span>        age.setAccessible(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">true</span>); <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 需要调用此方法且设置为 true</span><span>        age.set(instance, <span class="hljs-number" style="color: #986801; line-height: 26px;">66</span>);<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“获取私有字段的值: “</span> + age.get(instance));<span><span>    }<span><span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">获取所有字段: name<span>获取所有字段: age<span>================<span>获取指定字段: name<span>获取指定字段的值: per<span>获取私有字段的值: 66<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span> 395ms<span></span></span></span></span></span></span></span></span></code></pre><h2 id="使用注解和反射实现自动findViewById(案例)" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; bmin-height: 32px; line-height: 32px; border-bottom: solid 1px #000000; color: #35b378; display: inline-block; border-bottom-width: 0px; border-bottom-style: solid; border-color: #35b378; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; font-size: 23px; margin: 1em 0 0rem 0; padding: 0.5em 0; text-align: leftt; font-weight: bold;"><span class="prefix" style="display: none;"></span><span class="content">使用注解和反射实现自动findViewById(案例)</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们已经对注解和反射有了更清晰的认知，下面我们通过一个小案例来巩固我们的学习：使用注解和反射完成类似 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">butterknife</code> 的自动 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">findViewById</code> 的功能。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">新建一个空的 Android 工程，在工程目录下新建 <strong style="font-weight: bold; color: #35b378;">inject</strong> 目录，在此目录下新建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">InjectView</code> 的类和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">BindView</code> 的自定义注解，如：</p><h3 id="创建InjectView" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">创建InjectView</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">InjectView</code> 类通过反射完成 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">findViewById</code> 功能：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">InjectView</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">init</span><span class="hljs-params" style="line-height: 26px;">(Activity activity)</span> </span>{<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取 activity 的 class 对象</span><span>        Class&lt;? extends Activity&gt; aClass = activity.getClass();<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取 activity 的所以成员变量</span><span>        Field[] declaredFields = aClass.getDeclaredFields();<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 变量所以成员变量</span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">for</span> (Field field: declaredFields) {<span>            <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 判断属性是否加上了 @BindView 注解</span><span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span>(field.isAnnotationPresent(BindView.class)){<span>                <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取注解 BindView 对象</span><span>                BindView bindView = field.getAnnotation(BindView.class);<span>                <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取注解类型元素 id</span><span>                <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> id = bindView.value();<span>                <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 通过资源 id 找到对应的 view</span><span>                View view = activity.findViewById(id);<span>                <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 设置可以访问私有字段</span><span>                field.setAccessible(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">true</span>);<span>                <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>                    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 给字段赋值</span><span>                    field.set(activity,view);<span>                } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (IllegalAccessException e) {<span>                    e.printStackTrace();<span>                }<span>            }<span>        }<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h3 id="创建@BindView注解" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">创建@BindView注解</span><span class="suffix" style="display: none;"></span></h3><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Retention</span>(RetentionPolicy.RUNTIME)<span><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Target</span>(ElementType.FIELD)<span><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@interface</span> BindView {<span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@IdRes</span> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">value</span><span class="hljs-params" style="line-height: 26px;">()</span></span>; <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// @IdRes 只能传 id 资源</span><span>}<span></span></span></span></span></span></code></pre><h3 id="使用@BindView注解" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">使用@BindView注解</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">MainActivity</code> 里使用 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">@BindView</code> 注解，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">MainActivity</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">AppCompatActivity</span> </span>{<span><span>    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 使用注解</span><span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@BindView</span>(R.id.text_view)<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> TextView textView;<span><span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">onCreate</span><span class="hljs-params" style="line-height: 26px;">(Bundle savedInstanceState)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.onCreate(savedInstanceState);<span><span>        setContentView(R.layout.activity_main);<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 初始化 InjectView，完成自动 findViewById 功能</span><span>        InjectView.init(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>);<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 测试 R.id.text_view 是否自动赋值给 textView</span><span>        textView.setText(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“通过 @BindView 注解自动完成 findViewById”</span>);<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">运行结果，如图：</p><div style="width: 36%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations2.png" alt="no-shadow" title="small case" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">是不是很简单，一个类就完成了自动 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">findViewById</code> 的功能。</p><h2 id="动态代理" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; bmin-height: 32px; line-height: 32px; border-bottom: solid 1px #000000; color: #35b378; display: inline-block; border-bottom-width: 0px; border-bottom-style: solid; border-color: #35b378; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; font-size: 23px; margin: 1em 0 0rem 0; padding: 0.5em 0; text-align: leftt; font-weight: bold;"><span class="prefix" style="display: none;"></span><span class="content">动态代理</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">在了解动态代理之前，我们先来回顾下静态代理。</p><h3 id="静态代理" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">静态代理</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">代理模式给某一个对象提供一个代理对象，并由代理对象控制对原对象的引用，如，我们生活中常见的中介。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">代理模式一般会有3个角色，如图：</p><div style="width: 86%; margin:26px auto;" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations3.png" alt="no-shadow" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">抽象角色：指代理角色和真实角色对外提供的公共方法，一般为一个接口</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">真实角色：需要实现抽象角色接口，定义了真实角色所要实现的业务逻辑，以便供代理角色调用</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">代理角色：需要实现抽象角色接口，是真实角色的代理，通过真实角色的业务逻辑方法来实现抽象方法，并可以附加自己的操作</section></li></ul><h3 id="为什么要使用代理模式" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">为什么要使用代理模式</span><span class="suffix" style="display: none;"></span></h3><ul data-tool="mdnice编辑器" style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: disc;"><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">可以间接访问对象，防止直接访问对象来的不必要复杂性</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500; margin: 10px 0;">通过代理对象对访问进行控制</section></li></ul><h3 id="静态代理案例" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">静态代理案例</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">场景如下：</p><blockquote data-tool="mdnice编辑器" style="display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px; margin: 10px 5px; border-left: 3px solid #35b378; border-right: 0px solid #35b378; color: #616161; quotes: none; background: #FBF9FD;"><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0px; color: black; line-height: 26px;">小明可以在某网站上购买国内的东西，但是，不能买海外的东西，于是，他找了海外代购帮他买东西。</p></blockquote><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">如何用代码描述呢？根据代理模式的3个角色，我们分别定义1个接口2个类，如：<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">OrderService</code> 接口(抽象角色)、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ImplJapanOrderService</code> 类(真实角色)、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ProxyJapanOrder</code> 类(代理角色)</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">OrderService</code> 接口(抽象角色)，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">interface</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">OrderService</span> </span>{<span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">saveOrder</span><span class="hljs-params" style="line-height: 26px;">()</span></span>;<span>}<span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ImplJapanOrderService</code> 类(真实角色)，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 实现抽象角色接口</span><span><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">ImplJapanOrderService</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">OrderService</span> </span>{<span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">saveOrder</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        System.out.println(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“下单成功，订单号为：888888”</span>);<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> <span class="hljs-number" style="color: #986801; line-height: 26px;">888888</span>;<span>    }<span>}<span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ProxyJapanOrder</code> 类(代理角色)，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 实现抽象角色接口</span><span><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">ProxyJapanOrder</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">OrderService</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> OrderService orderService; <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 持有真实角色</span><span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> OrderService <span class="hljs-title" style="color: #4078f2; line-height: 26px;">getOrderService</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> orderService;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">setOrderService</span><span class="hljs-params" style="line-height: 26px;">(OrderService orderService)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.orderService = orderService;<span>    }<span><span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">saveOrder</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        System.out.print(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“日本代购订单，”</span>);<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> orderService.saveOrder(); <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 调用真实角色的行为方法</span><span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">在创建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Client</code> 类来测试我们的代码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">Client</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 日本代购订单</span><span>        OrderService orderJapan = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ImplJapanOrderService();<span>        ProxyJapanOrder proxyJapanOrder = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ProxyJapanOrder();<span>        proxyJapanOrder.setOrderService(orderJapan);<span>        proxyJapanOrder.saveOrder();<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">日本代购订单，下单成功，订单号为：888888<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span> 1s<span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">如果，需要购买韩国的东西，需要新增一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ImplKoreaOrderService</code> 类(韩国服务商) 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ProxyKoreaOrder</code> 类(韩国代理)，如还需要购买其他国家的东西，需要新增不同的类，则会出现静态代理对象量多、代码量大，从而导致代码复杂，可维护性差的问题，如是，我们需要使用动态代理。</p><h3 id="动态代理" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">动态代理</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">动态代理是在运行时才创建代理类和其实例，因此，我们可以传不同的真实角色，实现一个代理类完成多个真实角色的行为方法，当然，其效率比静态代理低。那么如何实现动态代理呢，JDK已为我们提供了 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Proxy</code> 类 和 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">InvocationHandler</code> 接口来完成这件事情。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们来创建一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ProxyDynamicOrder</code> 类(动态代理类)，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">ProxyDynamicOrder</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">InvocationHandler</span> </span>{<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> Object orderService; <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 持有真实角色</span><span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> Object <span class="hljs-title" style="color: #4078f2; line-height: 26px;">getOrderService</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> orderService;<span>    }<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">setOrderService</span><span class="hljs-params" style="line-height: 26px;">(Object orderService)</span> </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.orderService = orderService;<span>    }<span>    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 通过 Proxy 动态创建真实角色</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> Object <span class="hljs-title" style="color: #4078f2; line-height: 26px;">getProxyInstance</span><span class="hljs-params" style="line-height: 26px;">()</span></span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> Proxy.newProxyInstance(<span>                orderService.getClass().getClassLoader(),<span>                orderService.getClass().getInterfaces(),<span>                <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span><span>                );<span>    }<span><span>    <span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@Override</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> Object <span class="hljs-title" style="color: #4078f2; line-height: 26px;">invoke</span><span class="hljs-params" style="line-height: 26px;">(Object o, Method method, Object[] objects)</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span> Throwable </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> method.invoke(orderService, objects); <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 通过反射执行真实角色的行为方法</span><span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">在来看看，<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Client</code> 类里如何调用，代码如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">Client</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 静态代理模式</span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 国内订单</span><span>        OrderService order = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ImplOrderService();<span>        order.saveOrder();<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 日本代购订单</span><span>        OrderService orderJapan = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ImplJapanOrderService();<span>        ProxyJapanOrder proxyJapanOrder = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ProxyJapanOrder();<span>        proxyJapanOrder.setOrderService(orderJapan);<span>        proxyJapanOrder.saveOrder();<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 韩国代购订单</span><span>        OrderService orderKorea = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ImplKoreaOrderService();<span>        ProxyKoreaOrder proxyKoreaOrder = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ProxyKoreaOrder();<span>        proxyKoreaOrder.setOrderService(orderKorea);<span>        proxyKoreaOrder.saveOrder();<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 动态代理模式</span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 国内订单</span><span>        ProxyDynamicOrder proxyDynamicOrder = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ProxyDynamicOrder();<span>        OrderService orderService = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ImplOrderService();<span>        proxyDynamicOrder.setOrderService(orderService);<span>        OrderService orderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();<span>        orderService1.saveOrder();<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 日本代购订单</span><span>        OrderService japanOrderService = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ImplJapanOrderService();<span>        proxyDynamicOrder.setOrderService(japanOrderService);<span>        OrderService japanOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();<span>        japanOrderService1.saveOrder();<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 韩国代购订单</span><span>        OrderService koreaOrderService = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> ImplKoreaOrderService();<span>        proxyDynamicOrder.setOrderService(koreaOrderService);<span>        OrderService koreaOrderService1 = (OrderService) proxyDynamicOrder.getProxyInstance();<span>        koreaOrderService1.saveOrder();<span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 生成动态代理生成的class文件</span><span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">//ProxyUtil.generateClassFile(koreaOrderService.getClass(), koreaOrderService1.getClass().getSimpleName());</span><span><span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">运行结果，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">下单成功，订单号为：666666<span>日本代购订单，下单成功，订单号为：888888<span>韩国代购订单，下单成功，订单号为：666888<span>下单成功，订单号为：666666<span>下单成功，订单号为：888888<span>下单成功，订单号为：666888<span><span>BUILD SUCCESSFUL <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">in</span> 1s<span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">只需要一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ProxyDynamicOrder</code> 代理类即可完成 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ImplOrderService</code> 、 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ImplJapanOrderService</code> 、<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ImplKoreaOrderService</code> 真实角色提供的服务。</p><h3 id="动态代理原理" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; font-size: 20px; margin: 1.2em 0 1em; padding: 0; font-weight: bold; color: #35b378;"><span class="prefix" style="display: none;"></span><span class="content">动态代理原理</span><span class="suffix" style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们在 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">proxyDynamicOrder.getProxyInstance()</code> 代码上打个断点，通过调试模式发现，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations4.png" alt="no-shadow" title="proxy" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">代理类的名字是 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">$Proxy0@507</code>，为什么是这个名字，我们在编译后的目录里也找不到 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">$Proxy0@507</code> 类文件，如图：</p><div style="width: 56%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations5.png" alt="no-shadow" title="proxy" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">我们通过查看 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Proxy.newProxyInstance</code> 方法源码，可知，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-meta" style="color: #4078f2; line-height: 26px;">@CallerSensitive</span><span><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Object <span class="hljs-title" style="color: #4078f2; line-height: 26px;">newProxyInstance</span><span class="hljs-params" style="line-height: 26px;">(ClassLoader var0, Class&lt;?&gt;[] var1, InvocationHandler var2)</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span> IllegalArgumentException </span>{<span>    Objects.requireNonNull(var2);<span>    Class[] var3 = (Class[])var1.clone();<span>    SecurityManager var4 = System.getSecurityManager();<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span> (var4 != <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>) {<span>        checkProxyAccess(Reflection.getCallerClass(), var0, var3);<span>    }<span>    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取代理类的 class 对象</span><span>    Class var5 = getProxyClass0(var0, var3);<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span> (var4 != <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>) {<span>            checkNewProxyPermission(Reflection.getCallerClass(), var5);<span>        }<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 获取代理类的构造器</span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span> Constructor var6 = var5.getConstructor(constructorParams);<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span> (!Modifier.isPublic(var5.getModifiers())) {<span>            AccessController.doPrivileged(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> PrivilegedAction&lt;Void&gt;() {<span>                <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> Void <span class="hljs-title" style="color: #4078f2; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{<span>                    var6.setAccessible(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">true</span>);<span>                    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<span>                }<span>            });<span>        }<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 创建代理类的示例</span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> var6.newInstance(var2);<span>    } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (InstantiationException | IllegalAccessException var8) {<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> InternalError(var8.toString(), var8);<span>    } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (InvocationTargetException var9) {<span>        Throwable var7 = var9.getCause();<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span> (var7 <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">instanceof</span> RuntimeException) {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> (RuntimeException)var7;<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">else</span> {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> InternalError(var7.toString(), var7);<span>        }<span>    } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (NoSuchMethodException var10) {<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> InternalError(var10.toString(), var10);<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">然后，跟进 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">getProxyClass0(var0, var3)</code> 看看是如何获取代理类的 class 对象的，点击进入，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Class&lt;?&gt; getProxyClass0(ClassLoader var0, Class&lt;?&gt;… var1) {<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span> (var1.length &gt; <span class="hljs-number" style="color: #986801; line-height: 26px;">65535</span>) {<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> IllegalArgumentException(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“interface limit exceeded”</span>);<span>    } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">else</span> {<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 缓存了代理类的 class 对象</span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> (Class)proxyClassCache.get(var0, var1);<span>    }<span>}<span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">然后，我们来看看这个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">var1</code> 是个什么东西，我们往上找了找，果然发现，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// var1 就是我们实现的 InvocationHandler 接口</span><span><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">protected</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">Proxy</span><span class="hljs-params" style="line-height: 26px;">(InvocationHandler var1)</span> </span>{<span>    Objects.requireNonNull(var1);<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>.h = var1;<span>}<span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">然后，我们点进 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">proxyClassCache.get(var0, var1)</code> 方法，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations6.png" alt="no-shadow" title="proxy" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">使用关键代码 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">this.subKeyFactory.apply(var1, var2)</code> 去获取我们的代理类的 class 对象，我们进入 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">apply</code> 实现类 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">ProxyClassFactory</code>，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> Class&lt;?&gt; apply(ClassLoader var1, Class&lt;?&gt;[] var2) {<span>    IdentityHashMap var3 = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> IdentityHashMap(var2.length);<span>    Class[] var4 = var2;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> var5 = var2.length;<span><span>    …<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">if</span> (var16 == <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>) {<span>        var16 = <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“com.sun.proxy.”</span>;<span>    }<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">long</span> var19 = nextUniqueNumber.getAndIncrement();<span>    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 生成代理类的类名</span><span>    String var23 = var16 + <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“$Proxy”</span> + var19;<span>    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 生成代理类的字节码</span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">byte</span>[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>        <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 生成代理类的 class 对象</span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> Proxy.defineClass0(var1, var23, var22, <span class="hljs-number" style="color: #986801; line-height: 26px;">0</span>, var22.length);<span>    } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (ClassFormatError var14) {<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> IllegalArgumentException(var14.toString());<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">然后，我们点进 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">Proxy.defineClass0</code> 方法，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">native</span> Class&lt;?&gt; defineClass0(ClassLoader var0, String var1, <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">byte</span>[] var2, <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> var3, <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> var4);<span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">是一个 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">native</code> 方法，所以涉及到 C 或 C++ ，我们就不往后追踪。</p><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">那么，代理的 Class 文件到底存在哪儿呢，由一个类的生命周期，如图：</p><div style="width: 100%; margin:auto" data-tool="mdnice编辑器"><figure style="margin: 0; margin-top: 10px; margin-bottom: 10px; flex-direction: column; justify-content: center; align-items: center; display: block;"><img src="https://cdn.lishaoy.net/annotations-reflect/annotations7.png" alt="no-shadow" title="proxy" style="display: block; margin: 0 auto; max-width: 100%; border-radius: 6px;"></figure></div><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">代理的 Class 文件通过反射存在内存中，所以我们可以通过 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">byte[]</code> 写入文件，我们新建一个工具类来把内存中的 class 字节码写入文件，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">ProxyUtil</span> </span>{<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">void</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">generateClassFile</span><span class="hljs-params" style="line-height: 26px;">(Class aClass, String proxyName)</span> </span>{<span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">byte</span>[] proxyClassFile = ProxyGenerator.generateProxyClass(<span>                proxyName,<span>                <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> Class[]{aClass}<span>        );<span>        String path = aClass.getResource(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“.”</span>).getPath();<span>        System.out.println(path);<span>        FileOutputStream outputStream = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>;<span><span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>            outputStream = <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> FileOutputStream(path + proxyName + <span class="hljs-string" style="color: #50a14f; line-height: 26px;">“.class”</span>);<span>            outputStream.write(proxyClassFile);<span>            outputStream.flush();<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (IOException e) {<span>            e.printStackTrace();<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">finally</span> {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>                outputStream.close();<span>            } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (IOException e) {<span>                e.printStackTrace();<span>            }<span>        }<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">通过输出的 <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all; color: #35b378; box-shadow: none;">path</code> 路径，找到文件，如：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;">/Users/lishaoying/Documents/APP/Android/practice/annotation_reflect/anRePrDemo/proxyDemo/build/classes/java/main/net/lishaoy/proxydemo/service/impl/<span></span></code></pre><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">文件代码，如下：</p><pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 6px; padding: 0px; background: #fff;"><code class="hljs" style="overflow-x: auto; padding: 16px; color: #383a42; background: #fafafa; display: block; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; border-radius: 6px;"><span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 继承了 Proxy 实现了 ImplKoreaOrderService 接口</span><span><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">class</span> $<span class="hljs-title" style="color: #c18401; line-height: 26px;">Proxy0</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">extends</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">Proxy</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">implements</span> <span class="hljs-title" style="color: #c18401; line-height: 26px;">ImplKoreaOrderService</span> </span>{<span><span>    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 生成了各种方法</span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m1;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m8;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m3;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m2;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m5;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m4;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m7;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m9;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m0;<span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> Method m6;<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> $Proxy0(InvocationHandler var1) <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>  {<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>(var1);<span>    }<span><span>    …<span><span>    <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// 生成了 真实角色的 saveOrder 方法</span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">int</span> <span class="hljs-title" style="color: #4078f2; line-height: 26px;">saveOrder</span><span class="hljs-params" style="line-height: 26px;">()</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>  </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>            <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// h 是什？，点进去发现就是我们 传入的 InvocationHandler 接口</span><span>            <span class="hljs-comment" style="color: #a0a1a7; font-style: italic; line-height: 26px;">// m3 是什么？ 下面 static 代码块，就是我们的 saveOrder 方法</span><span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> (Integer)<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.h.invoke(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>, m3, (Object[])<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>);<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (RuntimeException | Error var2) {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> var2;<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (Throwable var3) {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> UndeclaredThrowableException(var3);<span>        }<span>    }<span><span>    …<span><span>    <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">final</span> Class <span class="hljs-title" style="color: #4078f2; line-height: 26px;">getClass</span><span class="hljs-params" style="line-height: 26px;">()</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throws</span>  </span>{<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">return</span> (Class)<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">super</span>.h.invoke(<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">this</span>, m7, (Object[])<span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">null</span>);<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (RuntimeException | Error var2) {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> var2;<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (Throwable var3) {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> UndeclaredThrowableException(var3);<span>        }<span>    }<span><span>    …<span><span>    <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">static</span> {<span>        <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">try</span> {<span>            m1 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“java.lang.Object”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“equals”</span>, Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“java.lang.Object”</span>));<span>            m8 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“notify”</span>);<span>            m3 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“saveOrder”</span>);<span>            m2 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“java.lang.Object”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“toString”</span>);<span>            m5 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“wait”</span>, Long.TYPE);<span>            m4 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“wait”</span>, Long.TYPE, Integer.TYPE);<span>            m7 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“getClass”</span>);<span>            m9 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“notifyAll”</span>);<span>            m0 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“java.lang.Object”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“hashCode”</span>);<span>            m6 = Class.forName(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“net.lishaoy.proxydemo.service.impl.ImplKoreaOrderService”</span>).getMethod(<span class="hljs-string" style="color: #50a14f; line-height: 26px;">“wait”</span>);<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (NoSuchMethodException var2) {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> NoSuchMethodError(var2.getMessage());<span>        } <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">catch</span> (ClassNotFoundException var3) {<span>            <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">throw</span> <span class="hljs-keyword" style="color: #a626a4; line-height: 26px;">new</span> NoClassDefFoundError(var3.getMessage());<span>        }<span>    }<span>}<span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre><h2 id="使用注解、反射、动态代理完成简单的Retrofit" data-tool="mdnice编辑器" style="margin-top: 30px; margin-bottom: 15px; bmin-height: 32px; line-height: 32px; border-bottom: solid 1px #000000; color: #35b378; display: inline-block; border-bottom-width: 0px; border-bottom-style: solid; border-color: #35b378; padding-top: 5px; padding-right: 0.5em; padding-left: 0.5em; font-size: 23px; margin: 1em 0 0rem 0; padding: 0.5em 0; text-align: leftt; font-weight: bold;"><span class="prefix" style="display: none;"></span><span class="content">使用注解、反射、动态代理完成简单的Retrofit</span><span class="suffix"></span></h2><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; line-height: 26px; color: black; margin: 1em 4px;">由于文章篇幅已经很长，且使用注解、反射、动态代理完成简单的 Retrofit 的案例代码过多，所以就不再这里展示，感兴趣的小伙伴可以去 <a href="https://github.com/persilee/android_practice" style="text-decoration: none; word-wrap: break-word; font-weight: bold; color: #35b378; border-bottom: 1px solid #35b378;" target="_blank" rel="noopener">GitHub</a> 查看源码。</p></section>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/annotations-reflect.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Android：写了这么多代码，你真的理解泛型吗</title>
      <link>https://h.lishaoy.net/generics.html</link>
      <guid>https://h.lishaoy.net/generics.html</guid>
      <pubDate>Fri, 24 Jul 2020 07:38:35 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/generics/generics.png&quot; class=&quot;full-image&quot; alt=&quot;Generics&quot; title=&quot;Generics&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;在我们的实际工作中 &lt;strong&gt;泛型(Generics)&lt;/strong&gt; 是无处不在的，我们也写过不少，看到的更多，如，源码、开源框架… 随处可见，但是，我们真正理解泛型吗？理解多少呢？例如：&lt;code&gt;Box&lt;/code&gt; 、&lt;code&gt;Box&amp;lt;Object&amp;gt;&lt;/code&gt; 、&lt;code&gt;Box&amp;lt;?&amp;gt;&lt;/code&gt; 、&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; 、&lt;code&gt;Box&amp;lt;? extends T&amp;gt;&lt;/code&gt; 、&lt;code&gt;Box&amp;lt;? super T&amp;gt;&lt;/code&gt; 之间的区别是什么？本篇文章将会对 &lt;strong&gt;泛型(Generics)&lt;/strong&gt; 进行全面的解析，让我们对泛型有更深入的理解。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/generics/generics.png" class="full-image" alt="Generics" title="Generics"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>在我们的实际工作中 <strong>泛型(Generics)</strong> 是无处不在的，我们也写过不少，看到的更多，如，源码、开源框架… 随处可见，但是，我们真正理解泛型吗？理解多少呢？例如：<code>Box</code> 、<code>Box&lt;Object&gt;</code> 、<code>Box&lt;?&gt;</code> 、<code>Box&lt;T&gt;</code> 、<code>Box&lt;? extends T&gt;</code> 、<code>Box&lt;? super T&gt;</code> 之间的区别是什么？本篇文章将会对 <strong>泛型(Generics)</strong> 进行全面的解析，让我们对泛型有更深入的理解。</p><hr><a id="more"></a><p>本篇文章的示例代码放在 <a href="https://github.com/persilee/android_practice" target="_blank" rel="noopener">Github</a> 上，所有知识点，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/generics/generic1.xmind.png" alt="no-shadow"></div><h2 id="Lucy-喜欢吃🍊（为什么要使用泛型）"><a href="#Lucy-喜欢吃🍊（为什么要使用泛型）" class="headerlink" title="Lucy 喜欢吃🍊（为什么要使用泛型）"></a>Lucy 喜欢吃🍊（为什么要使用泛型）</h2><p>首先，通过一个盘子装水果小故事来打开我们的泛型探索之旅（我们为什么要使用泛型），故事场景如下：</p><div class="note info"><p>Lucy 到 James 家做客，James 需要招待客人，且知道 Lucy 喜欢吃橘子🍊，于是使用水果盘装满了🍊来招待客人 </p></div> <p>这个场景怎么用代码表现呢，我们来新建几个类，如下：</p><p>Fruit：水果类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> entity;</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">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="string">"This is Fruit"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Apple：苹果类，继承水果类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> entity;</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">Apple</span> <span class="keyword">extends</span> <span class="title">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="string">" Apple 🍎"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Orange：橘子类，继承水果类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> entity;</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">Orange</span> <span class="keyword">extends</span> <span class="title">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="string">" Orange 🍊"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Plate：水果盘接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Plate</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(T t)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">get</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>FruitPlate：水果盘类，实现水果盘接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</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">FruitPlate</span> <span class="keyword">implements</span> <span class="title">Plate</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List items = <span class="keyword">new</span> ArrayList(<span class="number">6</span>);</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">        items.add(o);</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> Fruit <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> index = items.size() - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(index &gt;= <span class="number">0</span>) <span class="keyword">return</span> (Fruit) items.get(index);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>AiFruitPlate：智能水果盘，实现水果盘接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用泛型类定义</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;T&gt;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AiFruitPlate</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Plate</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;T&gt; fruits = <span class="keyword">new</span> ArrayList&lt;T&gt;(<span class="number">6</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">set</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">        fruits.add(t);</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> T <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> index = fruits.size() - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(index &gt;= <span class="number">0</span>) <span class="keyword">return</span> fruits.get(index);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Person：人类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> entity;</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">Person</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Lucy：Lucy类，继承 Person 类，她拥有吃橘子的能力 <code>eat</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> entity.Orange;</span><br><span class="line"><span class="keyword">import</span> entity.Person;</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">Lucy</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">(Orange orange)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"Lucy like eat"</span> + orange);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>James：James类，继承 Person 类，他拥有获取水果盘的能力 <code>getAiFruitPlate</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> entity.*;</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">James</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> FruitPlate <span class="title">getPlate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> FruitPlate();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> AiFruitPlate <span class="title">getAiFruitPlate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> AiFruitPlate();</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">addFruit</span><span class="params">(FruitPlate fruitPlate, Fruit fruit)</span> </span>&#123;</span><br><span class="line">        fruitPlate.set(fruit);</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">add</span><span class="params">(AiFruitPlate&lt;Orange&gt; aiFruitPlate, Orange orange)</span> </span>&#123;</span><br><span class="line">        aiFruitPlate.set(orange);</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>Scenario：测试类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> entity.*;</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">Scenario</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        scenario1();</span><br><span class="line">        scenario2();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//没有使用泛型</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">scenario1</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        James james = <span class="keyword">new</span> James();</span><br><span class="line">        Lucy lucy = <span class="keyword">new</span> Lucy();</span><br><span class="line">        FruitPlate fruitPlate = james.getPlate(); <span class="comment">// James 拿出水果盘</span></span><br><span class="line">        james.addFruit(fruitPlate,<span class="keyword">new</span> Orange()); <span class="comment">// James 往水果盘里装橘子</span></span><br><span class="line">        lucy.eat((Orange) fruitPlate.get()); <span class="comment">// 需要转型为 Orange</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//使用了泛型</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">scenario2</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        James james = <span class="keyword">new</span> James();</span><br><span class="line">        Lucy lucy = <span class="keyword">new</span> Lucy();</span><br><span class="line">        AiFruitPlate&lt;Orange&gt; aiFruitPlate = james.getAiFruitPlate(); <span class="comment">// James 拿出智能水果盘（知道你需要装橘子）</span></span><br><span class="line">        james.add(aiFruitPlate, <span class="keyword">new</span> Orange()); <span class="comment">// James 往水果盘里装橘子（如果，装的不是橘子会提醒）</span></span><br><span class="line">        lucy.eat(aiFruitPlate.get()); <span class="comment">// 不需要转型</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Lucy like eat  Orange 🍊</span><br><span class="line">Lucy like eat  Orange 🍊</span><br><span class="line"></span><br><span class="line">Process finished with <span class="built_in">exit</span> code 0</span><br></pre></td></tr></table></figure><p>我们可以很明显的看出，使用了泛型之后，不需要类型转换，如果，我们把 <code>scenario1()</code> 方法，稍微改下，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">scenario1</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    James james = <span class="keyword">new</span> James();</span><br><span class="line">    Lucy lucy = <span class="keyword">new</span> Lucy();</span><br><span class="line">    FruitPlate fruitPlate = james.getPlate();</span><br><span class="line">    james.addFruit(fruitPlate,<span class="keyword">new</span> Apple()); <span class="comment">//new Orange() 改成 new Apple()</span></span><br><span class="line">    lucy.eat((Orange) fruitPlate.get());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译器不会提示有问题，但是运行之后报错，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Exception <span class="keyword">in</span> thread <span class="string">"main"</span> java.lang.ClassCastException: entity.Apple cannot be cast to entity.Orange</span><br><span class="line">at Scenario.scenario1(Scenario.java:21)</span><br><span class="line">at Scenario.main(Scenario.java:7)</span><br><span class="line"></span><br><span class="line">Process finished with <span class="built_in">exit</span> code 1</span><br></pre></td></tr></table></figure><p>而，我们把 <code>scenario2()</code> （使用了泛型）做出同样的修改，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">scenario2</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    James james = <span class="keyword">new</span> James();</span><br><span class="line">    Lucy lucy = <span class="keyword">new</span> Lucy();</span><br><span class="line">    AiFruitPlate&lt;Orange&gt; aiFruitPlate = james.getAiFruitPlate();</span><br><span class="line">    james.add(aiFruitPlate, <span class="keyword">new</span> Apple());</span><br><span class="line">    lucy.eat(aiFruitPlate.get());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译器，会提示我们有错误，如图：</p><div style="width: 86%; margin:auto"><img src="https://cdn.lishaoy.net/generics/error.png" alt="error" title="error"></div><p>通过以上案例，很清晰的知道我们为什么要使用泛型，如下：</p><ul><li>消除类型转换</li><li>在编译时进行更强的类型检查</li><li>增加代码的复用性</li></ul><h2 id="泛型类-Generic-Class"><a href="#泛型类-Generic-Class" class="headerlink" title="泛型类(Generic Class)"></a>泛型类(Generic Class)</h2><p>泛型类是通过类型进行参数化的类，这样说可能不是很好理解，之后我们用代码演示。</p><h3 id="普通类-A-Simple-Class"><a href="#普通类-A-Simple-Class" class="headerlink" title="普通类(A Simple Class)"></a>普通类(A Simple Class)</h3><p>首先，我们来定义一个普通的类，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> definegeneric;</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">SimpleClass</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Object object;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getObject</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> object;</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">setObject</span><span class="params">(Object object)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.object = object;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>它的 <code>get</code> 、<code>set</code> 方法接受和返回一个 <code>Object</code>，所以，我们可以随意的传递任何类型。在编译时无法检查类型的使用，我们可以传入 <code>Integer</code> 且取出 <code>Integer</code>，也可以传入 <code>String</code> ，从而容易导致运行时错误。</p><h3 id="泛型类-A-Generic-Class"><a href="#泛型类-A-Generic-Class" class="headerlink" title="泛型类(A Generic Class)"></a>泛型类(A Generic Class)</h3><p>泛型类的定义格式如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">name</span>&lt;<span class="title">T1</span>,<span class="title">T2</span>,...,<span class="title">Tn</span>&gt;</span>&#123;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在类名之后的 <code>&lt;&gt;</code> 尖括号，称之为类型参数(类型变量)，定义一个泛型类就是使用 <code>&lt;&gt;</code> 给它定义类型参数：T1、T2 … Tn。</p><p>然后，我们把 <code>SimpleClass</code> 改成泛型类，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> definegeneric;</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">GenericClass</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> T t;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">getT</span><span class="params">()</span> </span>&#123;</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">public</span> <span class="keyword">void</span> <span class="title">setT</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.t = t;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>所以的 <code>object</code> 都替换成为 <code>T</code>，类型参数可以定义为任何的非基本类型，如：class类型、interface类型、数组类型、甚至是另一个类型参数。</p><h3 id="调用和实例化泛型类型-nvoking-and-Instantiating-a-Generic-Type"><a href="#调用和实例化泛型类型-nvoking-and-Instantiating-a-Generic-Type" class="headerlink" title="调用和实例化泛型类型(nvoking and Instantiating a Generic Type)"></a>调用和实例化泛型类型(nvoking and Instantiating a Generic Type)</h3><p>要想使用泛型类，必须执行泛型类调用，如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GenericClass&lt;String&gt; genericClass;</span><br></pre></td></tr></table></figure><p>泛型类的调用类似于方法的调用(传递了一个参数)，但是，我们没有将参数传递给方法，而是，将类型参数(String)传递给了 <code>GenericClass</code> 类本身。</p><p>此代码不会创建新的 <code>GenericClass</code> 对象，它只是声明了 <code>genericClass</code> 将保存对 <code>String</code> 的引用</p><p>要实例化此类，要使用 <code>new</code> 关键字，如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GenericClass&lt;String&gt; genericClass = <span class="keyword">new</span> GenericClass&lt;String&gt;();</span><br></pre></td></tr></table></figure><p>或者</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GenericClass&lt;String&gt; genericClass = <span class="keyword">new</span> GenericClass&lt;&gt;();</span><br></pre></td></tr></table></figure><p>在 Java SE 7 或更高的版本中，编译器可以从上下文推断出类型参数，因此，可以使用 <code>&lt;&gt;</code> 替换泛型类的构造函数所需的类型参数</p><h3 id="类型参数命名规范-Type-Parameter-Naming-Conventions"><a href="#类型参数命名规范-Type-Parameter-Naming-Conventions" class="headerlink" title="类型参数命名规范(Type Parameter Naming Conventions)"></a>类型参数命名规范(Type Parameter Naming Conventions)</h3><p>我们的类型参数是否一定要写成 <code>T</code> 呢，按照规范，类型参数名称是单个大写字母。</p><p>常用的类型参数名称有，如：</p><table><thead><tr><th style="text-align:center">类型参数</th><th style="text-align:center">含义</th></tr></thead><tbody><tr><td style="text-align:center">E</td><td style="text-align:center">Element</td></tr><tr><td style="text-align:center">K</td><td style="text-align:center">Key</td></tr><tr><td style="text-align:center">N</td><td style="text-align:center">Number</td></tr><tr><td style="text-align:center">V</td><td style="text-align:center">Value</td></tr><tr><td style="text-align:center">S,U,V…</td><td style="text-align:center">2nd, 3rd, 4th type</td></tr></tbody></table><h3 id="多类型参数-Multiple-Type-Parameters"><a href="#多类型参数-Multiple-Type-Parameters" class="headerlink" title="多类型参数(Multiple Type Parameters)"></a>多类型参数(Multiple Type Parameters)</h3><p>泛型类可以有多个类型参数，如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MultipleGeneric</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> K <span class="title">getKey</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> V <span class="title">getValue</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ImplMultipleGeneric</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">MultipleGeneric</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> K key;</span><br><span class="line">    <span class="keyword">private</span> V value;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ImplMultipleGeneric</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.key = key;</span><br><span class="line">        <span class="keyword">this</span>.value = value;</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> K <span class="title">getKey</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> key;</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> V <span class="title">getValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        MultipleGeneric&lt;String, Integer&gt; m1 = <span class="keyword">new</span> ImplMultipleGeneric&lt;String, Integer&gt;(<span class="string">"per"</span>,<span class="number">6</span>);</span><br><span class="line">        System.out.println(<span class="string">"key:"</span> + m1.getKey() + <span class="string">", value:"</span> + m1.getValue());</span><br><span class="line"></span><br><span class="line">        MultipleGeneric&lt;String,String&gt; m2 = <span class="keyword">new</span> ImplMultipleGeneric&lt;String, String&gt;(<span class="string">"per"</span>,<span class="string">"lsy"</span>);</span><br><span class="line">        System.out.println(<span class="string">"key:"</span> + m2.getKey() + <span class="string">", value:"</span> + m2.getValue());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出结果：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">key:per, value:6</span><br><span class="line">key:per, value:lsy</span><br><span class="line"></span><br><span class="line">Process finished with <span class="built_in">exit</span> code 0</span><br></pre></td></tr></table></figure><p>如上代码，<code>new ImplMultipleGeneric</code> 将 <code>K</code> 实例化为 <code>String</code>，将 <code>V</code> 实例化为 <code>Integer</code> ，因此， <code>ImplMultipleGeneric</code> 构造函数参数类型分别为 <code>String</code> 和 <code>Integer</code>,在编写 <code>new ImplMultipleGeneric</code> 代码时，编辑器会自动填写 <code>&lt;&gt;</code> 的值</p><p>由于，Java 编译器会从声明 <code>ImplMultipleGeneric</code> 推断出 <code>K</code> 和 <code>V</code> 的类型，因此我们可以简写为，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MultipleGeneric&lt;String, Integer&gt; m1 = <span class="keyword">new</span> ImplMultipleGeneric&lt;&gt;(<span class="string">"per"</span>,<span class="number">6</span>);</span><br><span class="line">System.out.println(<span class="string">"key:"</span> + m1.getKey() + <span class="string">", value:"</span> + m1.getValue());</span><br><span class="line"></span><br><span class="line">MultipleGeneric&lt;String,String&gt; m2 = <span class="keyword">new</span> ImplMultipleGeneric&lt;&gt;(<span class="string">"per"</span>,<span class="string">"lsy"</span>);</span><br><span class="line">System.out.println(<span class="string">"key:"</span> + m2.getKey() + <span class="string">", value:"</span> + m2.getValue());</span><br></pre></td></tr></table></figure><h2 id="泛型接口-Generic-Interface"><a href="#泛型接口-Generic-Interface" class="headerlink" title="泛型接口(Generic Interface)"></a>泛型接口(Generic Interface)</h2><p>定义泛型接口和定义泛型类相似(泛型类的技术可同用于泛型接口)，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">name</span>&lt;<span class="title">T1</span>,<span class="title">T2</span>,...,<span class="title">Tn</span>&gt;</span>&#123;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们来定义一个泛型接口，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> definegeneric;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Genertor</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>那么，如何实现一个泛型接口呢，我们使用两种方式来实现泛型接口，如下：</p><p>使用泛型类，实现泛型接口，且不指定确切的类型参数，所以，实现的 <code>next()</code> 返回值自动变成 <code>T</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> definegeneric.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> definegeneric.Genertor;</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">ImplGenertor</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Genertor</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用普通类，实现泛型接口，且指定确切的类型参数为 <code>String</code>，所以，实现的 <code>next()</code> 返回值自动变成 <code>String</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> definegeneric.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> definegeneric.Genertor;</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">ImplGenertor2</span> <span class="keyword">implements</span> <span class="title">Genertor</span>&lt;<span class="title">String</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="泛型方法-Generic-Methods"><a href="#泛型方法-Generic-Methods" class="headerlink" title="泛型方法(Generic Methods)"></a>泛型方法(Generic Methods)</h2><p>泛型方法使用了类型参数的方法，泛型方法比较独立，可以声明在 普通类、泛型类、普通接口、泛型接口中。</p><p>泛型方法定义格式，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;K, V&gt; <span class="function"><span class="keyword">boolean</span> <span class="title">compare</span><span class="params">(Pair&lt;K, V&gt; p1, Pair&lt;K, V&gt; p2)</span></span></span><br></pre></td></tr></table></figure><p>泛型方法的类型参数列表，在 <code>&lt;&gt;</code> 内，该列表必须在方法返回类型之前；对于静态的泛型方法，类型参数必须在 <code>static</code> 之后，方法返回类型之前。</p><h3 id="普通类里定义泛型方法-Generic-methods-in-a-Simple-Class"><a href="#普通类里定义泛型方法-Generic-methods-in-a-Simple-Class" class="headerlink" title="普通类里定义泛型方法(Generic methods in a Simple Class)"></a>普通类里定义泛型方法(Generic methods in a Simple Class)</h3><p>我们在普通类中定义泛型方法，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> methodgeneric;</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">MethodGeneric</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//定义一个泛型方法</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; <span class="function">T <span class="title">genericMethod</span><span class="params">(T...t)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> t[t.length/<span class="number">2</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">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">        MethodGeneric methodGeneric = <span class="keyword">new</span> MethodGeneric();</span><br><span class="line">        System.out.println(methodGeneric.&lt;String&gt;genericMethod(<span class="string">"java"</span>,<span class="string">"dart"</span>,<span class="string">"kotlin"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>methodGeneric.&lt;String&gt;genericMethod(&quot;java&quot;,&quot;dart&quot;,&quot;kotlin&quot;)</code> 通常可以省略掉 <code>&lt;&gt;</code> 的内容，编译器将推断出所需的类型，和调用普通方法一样，如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">methodGeneric.genericMethod(<span class="string">"java"</span>,<span class="string">"dart"</span>,<span class="string">"kotlin"</span>)</span><br></pre></td></tr></table></figure><h3 id="泛型类里定义泛型方法-Generic-methods-in-a-Generic-Class"><a href="#泛型类里定义泛型方法-Generic-methods-in-a-Generic-Class" class="headerlink" title="泛型类里定义泛型方法(Generic methods in a Generic Class)"></a>泛型类里定义泛型方法(Generic methods in a Generic Class)</h3><p>我们在泛型类中定义泛型方法，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><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">package</span> methodgeneric;</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">MethodGeneric2</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">"fruit"</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">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Apple</span> <span class="keyword">extends</span> <span class="title">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">"Apple"</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">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">"person"</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义了泛型类</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ShowClass</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">        <span class="comment">//定义了普通方法</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">show1</span><span class="params">(T t)</span></span>&#123;</span><br><span class="line">            System.out.println(t.toString());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//定义了泛型方法</span></span><br><span class="line">        <span class="keyword">public</span> &lt;E&gt; <span class="function"><span class="keyword">void</span> <span class="title">show2</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">            System.out.println(e.toString());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//定义了泛型方法</span></span><br><span class="line">        <span class="keyword">public</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">show3</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">            System.out.println(t.toString());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">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><br><span class="line">        Apple apple = <span class="keyword">new</span> Apple();</span><br><span class="line">        Person person = <span class="keyword">new</span> Person();</span><br><span class="line"></span><br><span class="line">        ShowClass&lt;Fruit&gt; showClass = <span class="keyword">new</span> ShowClass&lt;&gt;();</span><br><span class="line">        showClass.show1(apple);   <span class="comment">//可以放入 apple，因为 apple 是 fruit 的子类</span></span><br><span class="line">        showClass.show1(person); <span class="comment">//此时，编译器会报错，因为 ShowClass&lt;Fruit&gt; 已经限定类型</span></span><br><span class="line"></span><br><span class="line">        showClass.show2(apple); <span class="comment">//可以放入，泛型方法 &lt;E&gt; 可以是任何非基本类型</span></span><br><span class="line">        showClass.show2(person);<span class="comment">//可以放入，泛型方法 &lt;E&gt; 可以是任何非基本类型</span></span><br><span class="line"></span><br><span class="line">        showClass.show3(apple); <span class="comment">//可以放入，泛型方法 &lt;T&gt; 和泛型类中的 &lt;T&gt; 不是同一条 T，可以是任何非基本类型</span></span><br><span class="line">        showClass.show3(person); <span class="comment">//可以放入，泛型方法 &lt;T&gt; 和泛型类中的 &lt;T&gt; 不是同一条 T，可以是任何非基本类型</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在泛型类中定义泛型方法时，需要注意，泛型类里的泛型参数 <code>&lt;T&gt;</code> 和泛型方法里的泛型参数 <code>&lt;T&gt;</code> 不是同一个。</p><h2 id="限定类型参数-Bounded-Type-Parameters"><a href="#限定类型参数-Bounded-Type-Parameters" class="headerlink" title="限定类型参数(Bounded Type Parameters)"></a>限定类型参数(Bounded Type Parameters)</h2><p>我们经常看到类似 <code>public &lt;U extends Number&gt; void inspect(U u)</code> 的代码，<code>&lt;U extends Number&gt;</code> 就是限制类型参数，只对数字进行操作且只接受 <code>Number</code> 或其子类。</p><p>要声明一个限定的类型参数，需要在参数类型后加上 <code>extends</code> 关键字，然后是其上限类型(类或接口)。</p><h3 id="限定类型参数的泛型类-Generic-Class-of-Bounded-Type-Parameters"><a href="#限定类型参数的泛型类-Generic-Class-of-Bounded-Type-Parameters" class="headerlink" title="限定类型参数的泛型类(Generic Class of Bounded Type Parameters)"></a>限定类型参数的泛型类(Generic Class of Bounded Type Parameters)</h3><p>泛型类也可以使用限定类型参数，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> boundedgeneric;</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">BoundedClass</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">Comparable</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> T t;</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">setT</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.t = t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">min</span><span class="params">(T outter)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="keyword">this</span>.t.compareTo(outter) &gt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> outter;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BoundedClass&lt;String&gt; boundedClass = <span class="keyword">new</span> BoundedClass&lt;&gt;(); <span class="comment">//只能传入实现了 Comparable 接口的类型</span></span><br><span class="line">        boundedClass.setT(<span class="string">"iOS"</span>);</span><br><span class="line">        System.out.println(boundedClass.min(<span class="string">"android"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="限定类型参数的泛型方法-Generic-methods-of-Bounded-Type-Parameters"><a href="#限定类型参数的泛型方法-Generic-methods-of-Bounded-Type-Parameters" class="headerlink" title="限定类型参数的泛型方法(Generic methods of Bounded Type Parameters)"></a>限定类型参数的泛型方法(Generic methods of Bounded Type Parameters)</h3><p>泛型方法也可以使用限定类型参数，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> boundedgeneric;</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">BoundedGeneric</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T extends Comparable&gt; <span class="function">T <span class="title">min</span><span class="params">(T a, T b)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (a.compareTo(b) &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> a;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">return</span> b;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        System.out.println(BoundedGeneric.min(<span class="number">66</span>,<span class="number">666</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="多重限定-Multiple-Bounds"><a href="#多重限定-Multiple-Bounds" class="headerlink" title="多重限定(Multiple Bounds)"></a>多重限定(Multiple Bounds)</h3><p>限定类型参数，也可以为多个限定，如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;T extends B1 &amp; B2 &amp; B3&gt;</span><br></pre></td></tr></table></figure><p>多个限定参数，如果其中有类，类必须放在第一个位置，例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">A</span> </span>&#123; ... &#125;</span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">B</span> </span>&#123; ... &#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">C</span> </span>&#123; ... &#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">D</span> &lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">C</span> &amp; <span class="title">A</span> &amp; <span class="title">B</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="泛型，继承和子类型-Generics-Inheritance-and-Subtypes"><a href="#泛型，继承和子类型-Generics-Inheritance-and-Subtypes" class="headerlink" title="泛型，继承和子类型(Generics, Inheritance, and Subtypes)"></a>泛型，继承和子类型(Generics, Inheritance, and Subtypes)</h2><p>在前面的盘子装水果小故事里我们已经创建好了一些水果类，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"This is Fruit"</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">Apple</span> <span class="keyword">extends</span> <span class="title">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">" Apple 🍎"</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">Orange</span> <span class="keyword">extends</span> <span class="title">Fruit</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">" Orange 🍊"</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">QIOrange</span> <span class="keyword">extends</span> <span class="title">Orange</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> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"qi Orange 🍊"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>他们的继承关系，如图：</p><div style="width: 56%; margin:auto"><img src="https://cdn.lishaoy.net/generics/fruit.png" alt="no-shadow"></div><p>众所周知，我们可以把子类赋值给父类，例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Apple apple = <span class="keyword">new</span> Apple();</span><br><span class="line">Fruit fruit = <span class="keyword">new</span> Fruit();</span><br><span class="line">fruit = apple;</span><br></pre></td></tr></table></figure><p>泛型也是如此，我们定义一个水果盘子的泛型类，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></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">FruitPlateGen</span>&lt;<span class="title">Fruit</span>&gt; <span class="keyword">implements</span> <span class="title">Plate</span>&lt;<span class="title">Fruit</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;Fruit&gt; fruits = <span class="keyword">new</span> ArrayList&lt;&gt;(<span class="number">6</span>);</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(Fruit fruit)</span> </span>&#123;</span><br><span class="line">        fruits.add(fruit);</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> Fruit <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> index = fruits.size() - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(index &gt;= <span class="number">0</span>) <span class="keyword">return</span> fruits.get(index);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>所以，是 <code>Fruit</code> 的子类都可以放入水果盘里，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">FruitPlateGen&lt;Fruit&gt; fruitPlate = <span class="keyword">new</span> FruitPlateGen&lt;Fruit&gt;();</span><br><span class="line">fruitPlate.set(<span class="keyword">new</span> Apple());</span><br><span class="line">fruitPlate.set(<span class="keyword">new</span> Orange());</span><br></pre></td></tr></table></figure><p>现在，James 可以获取盘子，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">James</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> FruitPlateGen <span class="title">getAiFruitPlateGen</span><span class="params">(FruitPlateGen&lt;Fruit&gt; plate)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> FruitPlateGen();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如是，James 想获取放橘子的盘子，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">James james = <span class="keyword">new</span> James();</span><br><span class="line">james.getAiFruitPlateGen(<span class="keyword">new</span> FruitPlateGen&lt;Fruit&gt;()); <span class="comment">//获取成功</span></span><br><span class="line">james.getAiFruitPlateGen(<span class="keyword">new</span> FruitPlateGen&lt;Orange&gt;()); <span class="comment">//编译器报错</span></span><br></pre></td></tr></table></figure><p>虽然，<code>Orange</code> 是 <code>Fruit</code> 的子类，但是，<code>FruitPlateGen&lt;Orange&gt;</code> 不是 <code>FruitPlateGen&lt;Fruit&gt;</code> 的子类，所以，不能传递产生继承关系。</p><div style="width: 86%; margin:auto"><img src="https://cdn.lishaoy.net/generics/object.png" alt="no-shadow"></div><h3 id="泛型类和子类型-Generic-Classes-and-Subtyping"><a href="#泛型类和子类型-Generic-Classes-and-Subtyping" class="headerlink" title="泛型类和子类型(Generic Classes and Subtyping)"></a>泛型类和子类型(Generic Classes and Subtyping)</h3><p>我们可以通过继承(extends)或实现(implements)泛型类或接口，例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ExtendFruitPlate</span>&lt;<span class="title">Orange</span>&gt; <span class="keyword">extends</span> <span class="title">FruitPlateGen</span>&lt;<span class="title">Fruit</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时，<code>ExtendFruitPlate&lt;Orange&gt;</code> 就是 <code>FruitPlateGen&lt;Fruit&gt;</code> 的子类，James 再去拿盘子，就不会有错误提示：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">james.getAiFruitPlateGen(<span class="keyword">new</span> ExtendFruitPlate&lt;Orange&gt;());</span><br></pre></td></tr></table></figure><h2 id="通配符-Wildcards"><a href="#通配符-Wildcards" class="headerlink" title="通配符(Wildcards)"></a>通配符(Wildcards)</h2><p>我们经常看到类似 <code>List&lt;? extends Number&gt;</code> 的代码，<code>?</code> 就是通配符，表示未知类型。</p><h3 id="上限通配符-Upper-Bounded-Wildcards"><a href="#上限通配符-Upper-Bounded-Wildcards" class="headerlink" title="上限通配符(Upper Bounded Wildcards)"></a>上限通配符(Upper Bounded Wildcards)</h3><p>我们可以使用上限通配符来放宽对变量的限制，例如，上文提到的 <code>FruitPlateGen&lt;Fruit&gt;</code> 和 <code>FruitPlateGen&lt;Orange&gt;()</code> 就可以使用上限通配符。</p><p>我们来改写一下 <code>getAiFruitPlateGen</code> 方法，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> FruitPlateGen <span class="title">getAiFruitPlateGen2</span><span class="params">(FruitPlateGen&lt;? extends Fruit&gt; plate)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> FruitPlateGen();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这时候，James 想获取放橘子的盘子，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">James james = <span class="keyword">new</span> James();</span><br><span class="line">james.getAiFruitPlateGen2(<span class="keyword">new</span> FruitPlateGen&lt;Fruit&gt;()); <span class="comment">//获取成功</span></span><br><span class="line">james.getAiFruitPlateGen2(<span class="keyword">new</span> FruitPlateGen&lt;Orange&gt;()); <span class="comment">//获取成功</span></span><br></pre></td></tr></table></figure><p>上限通配符 <code>FruitPlateGen&lt;? extends Fruit&gt;</code> 匹配 <code>Fruit</code> 和 <code>Fruit</code> 的任何子类型，所以，我们可以传入 <code>Apple</code>、<code>Orange</code> 都没有问题。</p><h3 id="下限通配符-Lower-Bounded-Wildcards"><a href="#下限通配符-Lower-Bounded-Wildcards" class="headerlink" title="下限通配符(Lower Bounded Wildcards)"></a>下限通配符(Lower Bounded Wildcards)</h3><p>上限通配符将未知类型限定为该类型或其子类型，使用 <code>extends</code> 关键字，而下限通配符将未知类型限定为该类型或其父类型，使用 <code>super</code> 关键字。</p><p>我们再来宽展一下 <code>getAiFruitPlateGen</code> 方法，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> FruitPlateGen <span class="title">getAiFruitPlateGen3</span><span class="params">(FruitPlateGen&lt;? <span class="keyword">super</span> Apple&gt; plate)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> FruitPlateGen();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这时候，James 只能获取 <code>FruitPlateGen&lt;Fruit&gt;</code> 和 <code>FruitPlateGen&lt;Apple&gt;</code> 的盘子，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">James james = <span class="keyword">new</span> James();</span><br><span class="line">james.getAiFruitPlateGen3(<span class="keyword">new</span> FruitPlateGen&lt;Apple&gt;());</span><br><span class="line">james.getAiFruitPlateGen3(<span class="keyword">new</span> FruitPlateGen&lt;Fruit&gt;());</span><br></pre></td></tr></table></figure><p>下限通配符 <code>FruitPlateGen&lt;? super Apple&gt;</code> 匹配 <code>Apple</code> 和 <code>Apple</code> 的任何父类型，所以，我们可以传入 <code>Apple</code>、<code>Fruit</code>。</p><h3 id="通配符和子类型-Wildcards-and-Subtyping"><a href="#通配符和子类型-Wildcards-and-Subtyping" class="headerlink" title="通配符和子类型(Wildcards and Subtyping)"></a>通配符和子类型(Wildcards and Subtyping)</h3><p>在 <a href="https://h.lishaoy.net/generics.html#泛型，继承和子类型-Generics-Inheritance-and-Subtypes">泛型，继承和子类型</a> 章节有讲到，虽然，<code>Orange</code> 是 <code>Fruit</code> 的子类，但是，<code>FruitPlateGen&lt;Orange&gt;</code> 不是 <code>FruitPlateGen&lt;Fruit&gt;</code> 的子类。但是，你可以使用通配符在泛型类或接口之间创建关系。</p><p>我们再来回顾下 <code>Fruit</code> 的继承关系，如图：</p><div style="width: 56%; margin:auto"><img src="https://cdn.lishaoy.net/generics/fruit.png" alt="no-shadow"></div><p>代码，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Apple apple = <span class="keyword">new</span> Apple();</span><br><span class="line">Fruit fruit = apple;</span><br></pre></td></tr></table></figure><p>这个代码是没有问题的，<code>Fruit</code> 是 <code>Apple</code> 的父类，所以，可以把子类赋值给父类。</p><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Apple&gt; apples = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">List&lt;Fruit&gt; fruits = apples; <span class="comment">// 编辑器报错</span></span><br></pre></td></tr></table></figure><p>因为，<code>List&lt;Apple&gt;</code> 不是 <code>List&lt;Fruit&gt;</code> 的子类，实际上这两者无关，那么，它们的关系是什么？如图：</p><div style="width: 56%; margin:auto"><img src="https://cdn.lishaoy.net/generics/List%3C%3F%3E.png" alt="no-shadow"></div><p><code>List&lt;Apple&gt;</code> 和 <code>List&lt;Fruit&gt;</code> 的公共父级是 <code>List&lt;?&gt;</code>。</p><p>我们可以使用上下限通配符，在这些类之间创建关系，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Apple&gt; apples = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">List&lt;? extends Fruit&gt; fruits1 = apples; <span class="comment">// OK</span></span><br><span class="line">List&lt;? <span class="keyword">super</span> Apple&gt; fruits2 = apples; <span class="comment">// OK</span></span><br></pre></td></tr></table></figure><p>下图展示了上下限通配符声明的几个类的关系，如图：</p><div style="width: 56%; margin:auto"><img src="https://cdn.lishaoy.net/generics/List%3C%3F%3E1.png" alt="no-shadow"></div><h3 id="PECS原则-Producer-extends-Consumer-super"><a href="#PECS原则-Producer-extends-Consumer-super" class="headerlink" title="PECS原则(Producer extends Consumer super)"></a>PECS原则(Producer extends Consumer super)</h3><p>在上文中有 <code>FruitPlateGen</code> 水果盘子的类，我们尝试使用上下限通配符来实例化水果盘，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Apple apple = <span class="keyword">new</span> Apple();</span><br><span class="line">Orange orange = <span class="keyword">new</span> Orange();</span><br><span class="line">Fruit fruit = <span class="keyword">new</span> Fruit();</span><br><span class="line"></span><br><span class="line">FruitPlateGen&lt;? extends Fruit&gt; fruitPlateGen = <span class="keyword">new</span> FruitPlateGen&lt;&gt;();</span><br><span class="line">fruitPlateGen.set(apple); <span class="comment">// error</span></span><br><span class="line">fruitPlateGen.set(orange); <span class="comment">// error</span></span><br><span class="line">fruitPlateGen.set(fruit); <span class="comment">// error</span></span><br><span class="line">Fruit fruit1 = fruitPlateGen.get(); <span class="comment">// OK</span></span><br><span class="line">Orange orange1 = fruitPlateGen.get(); <span class="comment">// error</span></span><br><span class="line">Apple apple1 = fruitPlateGen.get(); <span class="comment">// error</span></span><br></pre></td></tr></table></figure><p>上限通配符无法 <code>set</code> 数据，但是，可以 <code>get</code> 数据且只能 <code>get</code> 到其上限 <code>Fruit</code>，所以，上限通配符可以安全的访问数据。</p><p>在来看一下代码，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">FruitPlateGen&lt;? <span class="keyword">super</span> Apple&gt; fruitPlateGen1 = <span class="keyword">new</span> FruitPlateGen&lt;&gt;();</span><br><span class="line">fruitPlateGen1.set(apple); <span class="comment">// OK</span></span><br><span class="line">fruitPlateGen1.set(orange); <span class="comment">// error</span></span><br><span class="line">fruitPlateGen1.set(fruit); <span class="comment">// error</span></span><br><span class="line">Object object = fruitPlateGen1.get(); <span class="comment">// OK</span></span><br><span class="line">Fruit fruit2 = fruitPlateGen1.get(); <span class="comment">// error</span></span><br><span class="line">Apple apple2 = fruitPlateGen1.get(); <span class="comment">// error</span></span><br><span class="line">Orange orange2 = fruitPlateGen1.get(); <span class="comment">// error</span></span><br></pre></td></tr></table></figure><p>下限通配符可以且只能 <code>set</code> 其下限 <code>Apple</code>，也可以 <code>get</code> 数据，但只能用 <code>Object</code> 接收(因为Object是所有类型的父类，这是一个特例)，所以，下限通配符可以安全的写入数据。</p><p>所以，在使用上下限通配符时，可以遵循以下准则：</p><ul><li>如果你只需要从集合中获得类型T , 使用&lt;? extends T&gt;通配符</li><li>如果你只需要将类型T放到集合中, 使用&lt;? super T&gt;通配符</li><li>如果你既要获取又要放置元素，则不使用任何通配符</li></ul><h2 id="类型擦除-Type-Erasure"><a href="#类型擦除-Type-Erasure" class="headerlink" title="类型擦除(Type Erasure)"></a>类型擦除(Type Erasure)</h2><p>Java 语言使用类型擦除机制实现了泛型，类型擦除机制，如下：</p><ul><li>编译器会把所有的类型参数替换为其边界(上下限)或 Object，因此，编译出的字节码中只包含普通类、接口和方法。</li><li>在必要时插入类型转换，已保持类型安全</li><li>生成桥接方法以在扩展泛型类时保持多态性</li></ul><h3 id="泛型类型的擦除-Erasure-of-Generic-Types"><a href="#泛型类型的擦除-Erasure-of-Generic-Types" class="headerlink" title="泛型类型的擦除(Erasure of Generic Types)"></a>泛型类型的擦除(Erasure of Generic Types)</h3><p>Java 编译器在擦除过程中，会擦除所有类型参数，如果类型参数是有界的，则替换为第一个边界，如果是无界的，则替换为 Object。</p><p>我们定义了一个泛型类，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> T data;</span><br><span class="line">  <span class="keyword">private</span> Node&lt;T&gt; next;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(T data, Node&lt;T&gt; next)</span> </span>&#123; <span class="keyword">this</span>.data = data;</span><br><span class="line">  <span class="keyword">this</span>.next = next;</span><br><span class="line">&#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> T <span class="title">getData</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> data; &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由于类型参数 <code>T</code> 是无界的，因此，Java 编译器将其替换为 Object，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> Object data;</span><br><span class="line">  <span class="keyword">private</span> Node next;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(Object data, Node next)</span> </span>&#123; <span class="keyword">this</span>.data = data;</span><br><span class="line">  <span class="keyword">this</span>.next = next;</span><br><span class="line">&#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> Object <span class="title">getData</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> data; &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们再来定义一个有界的泛型类，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">Comparable</span>&lt;<span class="title">T</span>&gt;&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> T data;</span><br><span class="line">  <span class="keyword">private</span> Node&lt;T&gt; next;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(T data, Node&lt;T&gt; next)</span> </span>&#123; <span class="keyword">this</span>.data = data;</span><br><span class="line">  <span class="keyword">this</span>.next = next;</span><br><span class="line">&#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> T <span class="title">getData</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> data; &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Java 编译器其替换为第一个边界 <code>Comparable</code>，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> Comparable data;</span><br><span class="line">  <span class="keyword">private</span> Node next;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(Comparable data, Node next)</span> </span>&#123; <span class="keyword">this</span>.data = data;</span><br><span class="line">  <span class="keyword">this</span>.next = next;</span><br><span class="line">&#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> Comparable <span class="title">getData</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> data; &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="泛型方法的擦除-Erasure-of-Generic-Methods"><a href="#泛型方法的擦除-Erasure-of-Generic-Methods" class="headerlink" title="泛型方法的擦除(Erasure of Generic Methods)"></a>泛型方法的擦除(Erasure of Generic Methods)</h3><p>Java 编译器同样会擦除泛型方法中的类型参数，例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function"><span class="keyword">int</span> <span class="title">count</span><span class="params">(T[] anArray, T elem)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">int</span> cnt = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (T e : anArray)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由于 <code>T</code> 是无界的，因此，Java 编译器将其替换为 Object，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">count</span><span class="params">(Object[] anArray, Object elem)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">int</span> cnt = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (Object e : anArray) <span class="keyword">if</span> (e.equals(elem))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如下代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Shape</span> </span>&#123;  ...  &#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Circle</span> <span class="keyword">extends</span> <span class="title">Shape</span> </span>&#123;  ...  &#125; </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Rectangle</span> <span class="keyword">extends</span> <span class="title">Shape</span> </span>&#123;  ...  &#125;</span><br></pre></td></tr></table></figure><p>有一个泛型方法，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span>&lt;T extends Shape&gt; <span class="function"><span class="keyword">void</span> <span class="title">draw</span><span class="params">(T shape)</span></span>&#123;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Java 编译器将用第一个边界 <code>Shape</code> 替换 <code>T</code>，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(Shape shape)</span></span>&#123;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="桥接方法-Bridge-Methods"><a href="#桥接方法-Bridge-Methods" class="headerlink" title="桥接方法(Bridge Methods)"></a>桥接方法(Bridge Methods)</h3><p>有时类型擦除会导致无法预料的情况，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></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">Node</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">public</span> T data;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(T data)</span> </span>&#123; <span class="keyword">this</span>.data = data; &#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setData</span><span class="params">(T data)</span> </span>&#123; </span><br><span class="line">    System.out.println(<span class="string">"Node.setData"</span>); </span><br><span class="line">    <span class="keyword">this</span>.data = data;</span><br><span class="line">  &#125; </span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyNode</span> <span class="keyword">extends</span> <span class="title">Node</span>&lt;<span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">MyNode</span><span class="params">(Integer data)</span> </span>&#123; <span class="keyword">super</span>(data); &#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setData</span><span class="params">(Integer data)</span> </span>&#123; </span><br><span class="line">    System.out.println(<span class="string">"MyNode.setData"</span>); </span><br><span class="line">    <span class="keyword">super</span>.setData(data);</span><br><span class="line">  &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类型擦除后，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></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">Node</span> </span>&#123;</span><br><span class="line">  <span class="keyword">public</span> Object data;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(Object data)</span> </span>&#123; <span class="keyword">this</span>.data = data; &#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setData</span><span class="params">(Object data)</span> </span>&#123; </span><br><span class="line">    System.out.println(<span class="string">"Node.setData"</span>); </span><br><span class="line">    <span class="keyword">this</span>.data = data;</span><br><span class="line">  &#125; </span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyNode</span> <span class="keyword">extends</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">MyNode</span><span class="params">(Integer data)</span> </span>&#123; <span class="keyword">super</span>(data); &#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setData</span><span class="params">(Integer data)</span> </span>&#123; </span><br><span class="line">    System.out.println(<span class="string">"MyNode.setData"</span>);</span><br><span class="line">    <span class="keyword">super</span>.setData(data);</span><br><span class="line">  &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时，Node 的方法变为 <code>setData(Object data)</code> 和 MyNode 的 <code>setData(Integer data)</code> 不会覆盖。</p><p>为了解决此问题并保留泛型类型的多态性，Java 编译器会生成一个桥接方法，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyNode</span> <span class="keyword">extends</span> <span class="title">Node</span> </span>&#123;</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">setData</span><span class="params">(Object data)</span> </span>&#123;</span><br><span class="line">      setData((Integer) data);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setData</span><span class="params">(Integer data)</span> </span>&#123; </span><br><span class="line">    System.out.println(<span class="string">"MyNode.setData"</span>); </span><br><span class="line">    <span class="keyword">super</span>.setData(data);</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>这样 Node 的方法 <code>setData(Object data)</code> 和 MyNode 生成的桥接方法 <code>setData(Object data)</code> 可以完成方法的覆盖。</p><h2 id="泛型的限制-Restrictions-on-Generics"><a href="#泛型的限制-Restrictions-on-Generics" class="headerlink" title="泛型的限制(Restrictions on Generics)"></a>泛型的限制(Restrictions on Generics)</h2><p>为了有效的使用泛型，需要考虑以下限制：</p><ul><li>无法实例化具有基本类型的泛型类型</li><li>无法创建类型参数的实例</li><li>无法声明类型为类型参数的静态字段</li><li>无法将Casts或instanceof与参数化类型一起使用</li><li>无法创建参数化类型的数组</li><li>无法创建，捕获或抛出参数化类型的对象</li><li>无法重载每个重载的形式参数类型都擦除为相同原始类型的方法</li></ul><h3 id="无法实例化具有基本类型的泛型类型"><a href="#无法实例化具有基本类型的泛型类型" class="headerlink" title="无法实例化具有基本类型的泛型类型"></a>无法实例化具有基本类型的泛型类型</h3><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Pair</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> K key;</span><br><span class="line">  <span class="keyword">private</span> V value;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Pair</span><span class="params">(K key, V value)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">this</span>.key = key;</span><br><span class="line">    <span class="keyword">this</span>.value = value; </span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建对象时，不能使用基本类型替换参数类型：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Pair&lt;<span class="keyword">int</span>, <span class="keyword">char</span>&gt; p = <span class="keyword">new</span> Pair&lt;&gt;(<span class="number">8</span>, <span class="string">'a'</span>); <span class="comment">// error</span></span><br></pre></td></tr></table></figure><h3 id="无法创建类型参数的实例"><a href="#无法创建类型参数的实例" class="headerlink" title="无法创建类型参数的实例"></a>无法创建类型参数的实例</h3><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;E&gt; <span class="function"><span class="keyword">void</span> <span class="title">append</span><span class="params">(List&lt;E&gt; list)</span> </span>&#123;</span><br><span class="line">   E elem = <span class="keyword">new</span> E(); <span class="comment">// error </span></span><br><span class="line">   list.add(elem);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="无法声明类型为类型参数的静态字段"><a href="#无法声明类型为类型参数的静态字段" class="headerlink" title="无法声明类型为类型参数的静态字段"></a>无法声明类型为类型参数的静态字段</h3><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></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">MobileDevice</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> T os; <span class="comment">// error</span></span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>类的静态字段是所有非静态对象共享的变量，因此，不允许使用类型参数的静态字段。</p><h3 id="无法将Casts或instanceof与参数化类型一起使用"><a href="#无法将Casts或instanceof与参数化类型一起使用" class="headerlink" title="无法将Casts或instanceof与参数化类型一起使用"></a>无法将Casts或instanceof与参数化类型一起使用</h3><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;E&gt; <span class="function"><span class="keyword">void</span> <span class="title">rtti</span><span class="params">(List&lt;E&gt; list)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (list <span class="keyword">instanceof</span> ArrayList&lt;Integer&gt;) &#123; <span class="comment">// error</span></span><br><span class="line">    ...</span><br><span class="line">  &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Java 编译器会擦除所有类型参数，所有，无法验证在运行时使用的参数化类型。</p><h3 id="无法创建参数化类型的数组"><a href="#无法创建参数化类型的数组" class="headerlink" title="无法创建参数化类型的数组"></a>无法创建参数化类型的数组</h3><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Integer&gt;[] arrayOfLists = <span class="keyword">new</span> List&lt;Integer&gt;[<span class="number">2</span>]; <span class="comment">// error</span></span><br></pre></td></tr></table></figure><h3 id="无法创建，捕获或抛出参数化类型的对象"><a href="#无法创建，捕获或抛出参数化类型的对象" class="headerlink" title="无法创建，捕获或抛出参数化类型的对象"></a>无法创建，捕获或抛出参数化类型的对象</h3><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MathException</span>&lt;<span class="title">T</span>&gt; <span class="keyword">extends</span> <span class="title">Exception</span> </span>&#123;  ...  &#125; <span class="comment">// error</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">QueueFullException</span>&lt;<span class="title">T</span>&gt; <span class="keyword">extends</span> <span class="title">Throwable</span></span>&#123; ... &#125; <span class="comment">// error</span></span><br></pre></td></tr></table></figure><h3 id="无法重载每个重载的形式参数类型都-擦除为相同原始类型的方法"><a href="#无法重载每个重载的形式参数类型都-擦除为相同原始类型的方法" class="headerlink" title="无法重载每个重载的形式参数类型都 擦除为相同原始类型的方法"></a>无法重载每个重载的形式参数类型都 擦除为相同原始类型的方法</h3><p>代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></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">Example</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(Set&lt;String&gt; strSet)</span> </span>&#123; &#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(Set&lt;Integer&gt; intSet)</span> </span>&#123; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>print(Set&lt;String&gt; strSet)</code> 和 <code>print(Set&lt;Integer&gt; intSet)</code> 在类型擦除后是完全相同的类型，所以，无法重载。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/generics.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Flutter(Flare) 最有趣用户交互动画没有之一</title>
      <link>https://h.lishaoy.net/flutter-flare.html</link>
      <guid>https://h.lishaoy.net/flutter-flare.html</guid>
      <pubDate>Fri, 03 Jul 2020 17:09:16 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/flutterFlare/flutter-flare-cover.png&quot; class=&quot;full-image&quot; alt=&quot;Flutter Flare&quot; title=&quot;Flutter Flare&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;2019年12月12日，&lt;strong&gt;Flutter&lt;/strong&gt; 在 &lt;strong&gt;Flutter Interact ‘19&lt;/strong&gt; 上发布了如何使用 &lt;strong&gt;Rive&lt;/strong&gt; 和 &lt;strong&gt;Flutter&lt;/strong&gt; 制作动态可交互的动画经验分享，我看了之后，觉得非常有趣，因此，写了3个小 demo，把它写成文章记录分享给大家。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/flutterFlare/flutter-flare-cover.png" class="full-image" alt="Flutter Flare" title="Flutter Flare"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>2019年12月12日，<strong>Flutter</strong> 在 <strong>Flutter Interact ‘19</strong> 上发布了如何使用 <strong>Rive</strong> 和 <strong>Flutter</strong> 制作动态可交互的动画经验分享，我看了之后，觉得非常有趣，因此，写了3个小 demo，把它写成文章记录分享给大家。</p><hr><a id="more"></a><h2 id="名词理解"><a href="#名词理解" class="headerlink" title="名词理解"></a>名词理解</h2><p>首先，我们来理解几个名词，不然后续文章，可能看着有些晕，如下：</p><ul><li>Flare：是 Flutter 的动画插件名称，完整名称是 <code>flare_flutter</code> 我们要在 <code>pubspec.yaml</code> 文件里引入</li><li>Rive：是制作 Flare 动画的<a href="https://rive.app/about-rive" target="_blank" rel="noopener">网站</a>，它既是一个网站也是制作工具，在此网站里有很多用户分享 Flare 动画供我们下载使用、Flare API使用文档、制作 Flare 动画的视频教程（大家也可以通过学习制作自己喜欢的动画）等</li></ul><h2 id="交互动画预览"><a href="#交互动画预览" class="headerlink" title="交互动画预览"></a>交互动画预览</h2><h3 id="登录交互动画"><a href="#登录交互动画" class="headerlink" title="登录交互动画"></a>登录交互动画</h3><p>登录交互动画，包含如下6种动画：</p><ul><li>idle：无任何操作时的状态（熊的身体会上下浮动和眨眼睛）</li><li>test：当我们在 email 输入框中输入时的状态（熊会看向输入框，且随着你输入的长度旋转头部）</li><li>hands_up：当我们在 password 输入框中输入时的状态 （熊会用手蒙上眼睛）</li><li>hands_down：当我们在 password 输入框输入完成时的状态 （熊会放下双手）</li><li>fail：当我们登录失败时的状态（熊会做出难过的表情）</li><li>success：当我们登录成功时的状态（熊会做出高兴的表情）</li></ul><p>以上6种状态，可以在 <strong>Rive</strong> 网站查看具体动画，<a href="https://rive.app/a/castor/files/flare/teddy-with-hands/preview" target="_blank" rel="noopener">点击进入查看</a></p><p>下面，我们来看看案例里实现动画效果</p><p>idle：无任何操作时的状态，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/flutterFlare/sign-in.gif" alt="no-shadow" title="idle"></div><p>test：当我们在 email 输入框中输入时的状态，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/flutterFlare/email.gif" alt="no-shadow" title="test"></div><p>hands_up：当我们在 password 输入框中输入时的状态，hands_down：当我们在 password 输入框输入完成时的状态，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/flutterFlare/password.gif" alt="no-shadow" title="hands_up &amp; hands_down"></div><p>fail：当我们登录失败时的状态，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/flutterFlare/failure.gif" alt="no-shadow" title="fail"></div><p>success：当我们登录成功时的状态，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/flutterFlare/successful.gif" alt="no-shadow" title="success"></div><h3 id="Button交互动画"><a href="#Button交互动画" class="headerlink" title="Button交互动画"></a>Button交互动画</h3><p>button 交互动画，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/flutterFlare/button.gif" alt="no-shadow" title="button"></div><h3 id="Menu交互动画"><a href="#Menu交互动画" class="headerlink" title="Menu交互动画"></a>Menu交互动画</h3><p>menu 交互动画，如图：</p><div style="width: 100%; margin:auto"><img src="https://cdn.lishaoy.net/flutterFlare/menu.gif" alt="no-shadow" title="menu"></div><p>以上所有动画，也可以 <a href="https://www.bilibili.com/video/BV14p4y1U7YN/" target="_blank" rel="noopener">点击观看视频</a></p><h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><p>如何用代码实现，分为以下2个步骤：</p><ul><li>引入插件和资源：引入相关插件 <code>flare_flutter</code> 、 <code>smart_flare</code></li><li>编写代码：编写相关代码</li></ul><h3 id="引入插件和资源"><a href="#引入插件和资源" class="headerlink" title="引入插件和资源"></a>引入插件和资源</h3><p>引入插件和资源，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">dependencies:</span><br><span class="line">  ...</span><br><span class="line">  flare_flutter: ^2.0.4  <span class="comment"># flare 插件</span></span><br><span class="line">  smart_flare: any  <span class="comment"># 对 flare API进行封装的插件，使用少量的代码即可实现交互动画</span></span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line">assets:</span><br><span class="line">  ...</span><br><span class="line">  - assets/Teddy.flr</span><br><span class="line">  - assets/button-animation.flr</span><br><span class="line">  - assets/slideout-menu.flr</span><br><span class="line">  ...</span><br></pre></td></tr></table></figure><h3 id="编写代码"><a href="#编写代码" class="headerlink" title="编写代码"></a>编写代码</h3><p>由于，登录交互动画稍复杂一些，在此就不展示实现的代码，如感兴趣，可<a href="https://github.com/persilee/flutter_pro/blob/master/lib/demo/flare_demo/flare_sign_in_demo.dart" target="_blank" rel="noopener">移步GitHub查看源码</a></p><h3 id="Button交互动画代码实现"><a href="#Button交互动画代码实现" class="headerlink" title="Button交互动画代码实现"></a>Button交互动画代码实现</h3><p>button 交互动画代码实现如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:smart_flare/actors/smart_flare_actor.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:smart_flare/models.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FlareButtonDemo</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  _FlareButtonDemoState createState() =&gt; _FlareButtonDemoState();</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">_FlareButtonDemoState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">FlareButtonDemo</span>&gt; </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">var</span> animationWidth = <span class="number">295.0</span>;</span><br><span class="line">    <span class="keyword">var</span> animationHeight = <span class="number">251.0</span>;</span><br><span class="line">    <span class="keyword">var</span> animationWidthThirds = animationWidth / <span class="number">3</span>;</span><br><span class="line">    <span class="keyword">var</span> halfAnimationHeight = animationHeight / <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> activeAreas = [</span><br><span class="line"></span><br><span class="line">      ActiveArea(</span><br><span class="line">        area: Rect.fromLTWH(<span class="number">0</span>, <span class="number">0</span>, animationWidthThirds, halfAnimationHeight),</span><br><span class="line">        debugArea: <span class="keyword">false</span>,</span><br><span class="line">        guardComingFrom: [<span class="string">'deactivate'</span>],</span><br><span class="line">        animationName: <span class="string">'camera_tapped'</span>,</span><br><span class="line">      ),</span><br><span class="line"></span><br><span class="line">      ActiveArea(</span><br><span class="line">          area: Rect.fromLTWH(animationWidthThirds, <span class="number">0</span>, animationWidthThirds, halfAnimationHeight),</span><br><span class="line">          debugArea: <span class="keyword">false</span>,</span><br><span class="line">          guardComingFrom: [<span class="string">'deactivate'</span>],</span><br><span class="line">          animationName: <span class="string">'pulse_tapped'</span>),</span><br><span class="line"></span><br><span class="line">      ActiveArea(</span><br><span class="line">          area: Rect.fromLTWH(animationWidthThirds * <span class="number">2</span>, <span class="number">0</span>, animationWidthThirds, halfAnimationHeight),</span><br><span class="line">          debugArea: <span class="keyword">false</span>,</span><br><span class="line">          guardComingFrom: [<span class="string">'deactivate'</span>],</span><br><span class="line">          animationName: <span class="string">'image_tapped'</span>),</span><br><span class="line"></span><br><span class="line">      ActiveArea(</span><br><span class="line">          area: Rect.fromLTWH(<span class="number">0</span>, animationHeight / <span class="number">2</span>, animationWidth, animationHeight / <span class="number">2</span>),</span><br><span class="line">          debugArea: <span class="keyword">false</span>,</span><br><span class="line">          animationsToCycle: [<span class="string">'activate'</span>, <span class="string">'deactivate'</span>],</span><br><span class="line">          onAreaTapped: () &#123;</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">'Button tapped!'</span>);</span><br><span class="line">          &#125;)</span><br><span class="line"></span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(<span class="string">'Flare Button Demo'</span>),</span><br><span class="line">      ),</span><br><span class="line">      body: Container(</span><br><span class="line">        decoration: BoxDecoration(</span><br><span class="line">          gradient: LinearGradient(</span><br><span class="line">              begin: Alignment.topCenter,</span><br><span class="line">              end: Alignment.bottomCenter,</span><br><span class="line">              colors: [</span><br><span class="line">                Color(<span class="number">0x3fffeb3b</span>),</span><br><span class="line">                Colors.orange,</span><br><span class="line">              ]),</span><br><span class="line">        ),</span><br><span class="line">        child: Align(</span><br><span class="line">          alignment: Alignment.bottomCenter,</span><br><span class="line">          child: SmartFlareActor(</span><br><span class="line">            width: animationWidth,</span><br><span class="line">            height: animationHeight,</span><br><span class="line">            filename: <span class="string">'assets/button-animation.flr'</span>,</span><br><span class="line">            startingAnimation: <span class="string">'deactivate'</span>,</span><br><span class="line">            activeAreas: activeAreas,</span><br><span class="line">          ),</span><br><span class="line">        ),</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Menu交互动画代码实现"><a href="#Menu交互动画代码实现" class="headerlink" title="Menu交互动画代码实现"></a>Menu交互动画代码实现</h3><p>menu 交互动画代码实现，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:smart_flare/smart_flare.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FlareSidebarMenuDemo</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="built_in">print</span>(MediaQuery.of(context).size.height);</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      body: Container(</span><br><span class="line">        child: Align(</span><br><span class="line">          alignment: Alignment.centerRight,</span><br><span class="line">          child: PanFlareActor(</span><br><span class="line">            width: MediaQuery.of(context).size.width / <span class="number">2.366</span>,</span><br><span class="line">            height: MediaQuery.of(context).size.height,</span><br><span class="line">            filename: <span class="string">'assets/slideout-menu.flr'</span>,</span><br><span class="line">            openAnimation: <span class="string">'open'</span>,</span><br><span class="line">            closeAnimation: <span class="string">'close'</span>,</span><br><span class="line">            direction: ActorAdvancingDirection.RightToLeft,</span><br><span class="line">            threshold: <span class="number">20.0</span>,</span><br><span class="line">            reverseOnRelease: <span class="keyword">true</span>,</span><br><span class="line">            completeOnThresholdReached: <span class="keyword">true</span>,</span><br><span class="line">            activeAreas: [</span><br><span class="line">              RelativePanArea(</span><br><span class="line">                  area: Rect.fromLTWH(<span class="number">0</span>, <span class="number">.7</span>, <span class="number">1.0</span>, <span class="number">.3</span>), debugArea: <span class="keyword">false</span>),</span><br><span class="line">            ],</span><br><span class="line">          ),</span><br><span class="line">        ),</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上3个交互动画案例的源码，放在了我2年前写的一个 <a href="https://github.com/persilee/flutter_pro" target="_blank" rel="noopener">Flutter案例</a> 的项目里了，此项目现已维护起来，以后会长期更新，感兴趣的小伙伴可以收藏，没事时来看看可能会有新的发现 😲</p><p>此篇文章到此结束，下篇文章计划给大家分享，Flutter 里的路由，会总结归纳所有的路由使用方法，最后来封装一个优秀的路由管理类。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/flutter-flare.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>FutureBuilder and StreamBuilder 优雅的构建高质量项目</title>
      <link>https://h.lishaoy.net/futrueBuilder-streamBuilder.html</link>
      <guid>https://h.lishaoy.net/futrueBuilder-streamBuilder.html</guid>
      <pubDate>Mon, 29 Jun 2020 11:59:16 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/fureBuilderStreamBuilder/cover.png&quot; class=&quot;full-image&quot; alt=&quot;Flutter&quot; title=&quot;Flutter&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;本篇文章将介绍从 &lt;code&gt;setState&lt;/code&gt; 开始，到 &lt;code&gt;futureBuilder&lt;/code&gt; 、 &lt;code&gt;streamBuilder&lt;/code&gt; 来优雅的构建你的高质量项目，而不引发 &lt;code&gt;setState&lt;/code&gt; 带来的副作用，如对文章感兴趣，请 &lt;a href=&quot;https://github.com/persilee/flutter_pro&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;点击查看源码&lt;/a&gt;。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/fureBuilderStreamBuilder/cover.png" class="full-image" alt="Flutter" title="Flutter"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>本篇文章将介绍从 <code>setState</code> 开始，到 <code>futureBuilder</code> 、 <code>streamBuilder</code> 来优雅的构建你的高质量项目，而不引发 <code>setState</code> 带来的副作用，如对文章感兴趣，请 <a href="https://github.com/persilee/flutter_pro" target="_blank" rel="noopener">点击查看源码</a>。</p><hr><a id="more"></a><h2 id="基础的setState更新数据"><a href="#基础的setState更新数据" class="headerlink" title="基础的setState更新数据"></a>基础的setState更新数据</h2><p>首先，我们使用基础的 <code>StatefulWidget</code> 来创建页面，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="class"><span class="keyword">class</span> <span class="title">BaseStatefulDemo</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  _BaseStatefulDemoState createState() =&gt; _BaseStatefulDemoState();</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">_BaseStatefulDemoState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">BaseStatefulDemo</span>&gt; </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Container();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，我们使用 <code>Future</code> 来创建一些数据，来模拟网络请求，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><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">Future&lt;<span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;&gt; _getListData() <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="keyword">await</span> Future.delayed(<span class="built_in">Duration</span>(seconds: <span class="number">1</span>)); <span class="comment">// 1秒之后返回数据</span></span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;.generate(<span class="number">10</span>, (index) =&gt; <span class="string">'<span class="subst">$index</span> content'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 <code>initState()</code> 方法中调用 <code>_getListData()</code> 来初始化数据，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="built_in">List</span>&lt;<span class="built_in">String</span>&gt; _pageData = <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;();</span><br><span class="line"></span><br><span class="line"><span class="meta">@override</span></span><br><span class="line"><span class="keyword">void</span> initState() &#123;</span><br><span class="line">  _getListData().then((data) =&gt; setState(() &#123;</span><br><span class="line">            _pageData = data;</span><br><span class="line">          &#125;));</span><br><span class="line">  <span class="keyword">super</span>.initState();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用 <code>ListView.builder</code> 来处理这些数据构建UI，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="meta">@override</span></span><br><span class="line">Widget build(BuildContext context) &#123;</span><br><span class="line">  <span class="keyword">return</span> Scaffold(</span><br><span class="line">    appBar: AppBar(</span><br><span class="line">      title: Text(<span class="string">'Base Stateful Demo'</span>),</span><br><span class="line">    ),</span><br><span class="line">    body: ListView.builder(</span><br><span class="line">      itemCount: _pageData.length,</span><br><span class="line">      itemBuilder: (buildContext, index) &#123;</span><br><span class="line">        <span class="keyword">return</span> Column(</span><br><span class="line">          children: &lt;Widget&gt;[</span><br><span class="line">            ListTile(</span><br><span class="line">              title: Text(_pageData[index]),</span><br><span class="line">            ),</span><br><span class="line">            Divider(),</span><br><span class="line">          ],</span><br><span class="line">        );</span><br><span class="line">      &#125;,</span><br><span class="line">    ),</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后，我们就可以看到界面了 😎 ，如图：</p><div style="width: 36%; margin:auto"><img src="https://cdn.lishaoy.net/fureBuilderStreamBuilder/list-data.png" alt="no-shadow" title="list data"></div><p>当然，你也可以将 <strong>UI</strong> 显示单独提取成一个方法，方便后期维护，使代码层次更清晰，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@override</span></span><br><span class="line">Widget build(BuildContext context) &#123;</span><br><span class="line">  <span class="keyword">return</span> Scaffold(</span><br><span class="line">    appBar: AppBar(</span><br><span class="line">      title: Text(<span class="string">'Base Stateful Demo'</span>),</span><br><span class="line">    ),</span><br><span class="line">    body: ListView.builder(</span><br><span class="line">      itemCount: _pageData.length,</span><br><span class="line">      itemBuilder: (buildContext, index) &#123;</span><br><span class="line">        <span class="keyword">return</span> getListDataUi(<span class="built_in">int</span> index);</span><br><span class="line">      &#125;,</span><br><span class="line">    ),</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Widget getListDataUi(<span class="built_in">int</span> index) &#123;</span><br><span class="line">  <span class="keyword">return</span> Column(</span><br><span class="line">              children: &lt;Widget&gt;[</span><br><span class="line">                ListTile(</span><br><span class="line">                  title: Text(_pageData[index]),</span><br><span class="line">                ),</span><br><span class="line">                Divider(),</span><br><span class="line">              ],</span><br><span class="line">            );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>继续，我们来完善它，正常从后端获取数据，后端应该会给我们返回不同信息，根据这些信息需要处理不同的状态，如：</p><ul><li>BusyState(加载中)：我们在界面上显示一个加载指示器</li><li>DataFetchedState(数据加载完成)：我们延迟2秒，来模拟数据加载完成</li><li>ErrorState(错误)：显示错误提示</li><li>NoData(没有数据)：请求成功，但没有数据，显示提示</li></ul><p>先来处理 <strong>BusyState</strong> 加载指示器，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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="built_in">bool</span> <span class="keyword">get</span> _fetchingData =&gt; _pageData == <span class="keyword">null</span>; <span class="comment">// 判断数据是否为空</span></span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(<span class="string">'Base Stateful Demo'</span>),</span><br><span class="line">      ),</span><br><span class="line">      body: _fetchingData</span><br><span class="line">          ? Center(</span><br><span class="line">              child: CircularProgressIndicator( <span class="comment">// 加载指示器 </span></span><br><span class="line">                valueColor: AlwaysStoppedAnimation&lt;Color&gt;(Colors.yellow), <span class="comment">// 设置指示器颜色</span></span><br><span class="line">                backgroundColor: Colors.yellow[<span class="number">100</span>],  <span class="comment">// 设置背景色</span></span><br><span class="line">              ),</span><br><span class="line">            )</span><br><span class="line">          : ListView.builder(</span><br><span class="line">              itemCount: _pageData.length,</span><br><span class="line">              itemBuilder: (buildContext, index) &#123;</span><br><span class="line">                <span class="keyword">return</span> getListDataUi(index);</span><br><span class="line">              &#125;,</span><br><span class="line">            ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>效果如图：</p><div style="width: 36%; margin:auto"><img src="https://cdn.lishaoy.net/fureBuilderStreamBuilder/indicator.png" alt="no-shadow" title="indicator"></div><p>接着，我们来处理 <strong>ErrorState</strong> ，我给 <code>_getListData()</code> 添加 <code>hasError</code> 参数来模拟后端返回的错误，如下</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">Future&lt;<span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;&gt; _getListData(&#123;<span class="built_in">bool</span> hasError = <span class="keyword">false</span>&#125;) <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="keyword">await</span> Future.delayed(<span class="built_in">Duration</span>(seconds: <span class="number">1</span>)); <span class="comment">// 1秒之后返回数据</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (hasError) &#123;</span><br><span class="line">    <span class="keyword">return</span> Future.error(<span class="string">'获取数据出现问题，请再试一次'</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;.generate(<span class="number">10</span>, (index) =&gt; <span class="string">'<span class="subst">$index</span> content'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，在 <code>initState()</code> 方法中捕获异常更新数据，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">@override</span></span><br><span class="line"><span class="keyword">void</span> initState() &#123;</span><br><span class="line">  _getListData(hasError: <span class="keyword">true</span>)</span><br><span class="line">      .then((data) =&gt; setState(() &#123;</span><br><span class="line">            _pageData = data;</span><br><span class="line">          &#125;))</span><br><span class="line">      .catchError((error) =&gt; setState(() &#123;</span><br><span class="line">            _pageData = [error];</span><br><span class="line">          &#125;));</span><br><span class="line">  <span class="keyword">super</span>.initState();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>效果如图( <em>当然这里可以使用一个错误页面来展示</em> )：</p><div style="width: 36%; margin:auto"><img src="https://cdn.lishaoy.net/fureBuilderStreamBuilder/error.png" alt="no-shadow" title="error"></div><p>接着，我们来处理 <strong>NoData</strong> ，我给 <code>_getListData()</code> 添加 <code>hasData</code> 参数来模拟后端返回空数据，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">Future&lt;<span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;&gt; _getListData(</span><br><span class="line">    &#123;<span class="built_in">bool</span> hasError = <span class="keyword">false</span>, <span class="built_in">bool</span> hasData = <span class="keyword">true</span>&#125;) <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="keyword">await</span> Future.delayed(<span class="built_in">Duration</span>(seconds: <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (hasError) &#123;</span><br><span class="line">    <span class="keyword">return</span> Future.error(<span class="string">'获取数据出现问题，请再试一次'</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (!hasData) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;.generate(<span class="number">10</span>, (index) =&gt; <span class="string">'<span class="subst">$index</span> content'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，在 <code>initState()</code> 方法更新数据，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="meta">@override</span></span><br><span class="line"><span class="keyword">void</span> initState() &#123;</span><br><span class="line">  _getListData(hasError: <span class="keyword">false</span>, hasData: <span class="keyword">false</span>)</span><br><span class="line">      .then((data) =&gt; setState(() &#123;</span><br><span class="line">            <span class="keyword">if</span> (data.length == <span class="number">0</span>) &#123;</span><br><span class="line">              data.add(<span class="string">'No data fount'</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            _pageData = data;</span><br><span class="line">          &#125;))</span><br><span class="line">      .catchError((error) =&gt; setState(() &#123;</span><br><span class="line">            _pageData = [error];</span><br><span class="line">          &#125;));</span><br><span class="line">  <span class="keyword">super</span>.initState();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>效果如图：</p><div style="width: 36%; margin:auto"><img src="https://cdn.lishaoy.net/fureBuilderStreamBuilder/no-data.png" alt="no-shadow" title="error"></div><div class="note success"><p>这就是通过 <code>setState()</code> 来更新数据，是不是很简单，通常情况下我们这么使用是没什么问题，但是，如果我们的页面足够复杂，要处理的状态足够多，我们需要使用更多的 <code>setState()</code> ，意味着我们要更多的代码来更新数据，而且，我们每次 <code>setState()</code> 的时候 <code>build()</code> 方法就会重新执行一次( <em>这就是上文提到的副作用</em> )。</p><p>其实，<strong>Flutter</strong> 已经提供了更优雅的方式来更新我们的数据及处理状态，它就是我们接下来要介绍的 <code>futureBuilder</code>。</p></div><h2 id="FutureBuilder"><a href="#FutureBuilder" class="headerlink" title="FutureBuilder"></a>FutureBuilder</h2><p><code>FutureBuilder</code> 通过 <strong>future:</strong> 参数可以接收一个 <code>Future</code> ，并且通过 <strong>builder:</strong> 参数来构建 <strong>UI</strong> ，<strong>builder:</strong> 参数是一个函数，它提供了一个 <code>snapshot</code> 参数里面带着我们需要的状态和数据。</p><p>接下来，我们将上面的 <code>StatefulWidget</code> 改成 <code>StatelessWidget</code> ，并使用 <code>FutureBuilder</code> 替换，如下:</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FutureBuilderDemo</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(<span class="string">'Future Builder Demo'</span>),</span><br><span class="line">      ),</span><br><span class="line">      body: FutureBuilder(</span><br><span class="line">        future: _getListData(),</span><br><span class="line">        builder: (buildContext, snapshot) &#123;</span><br><span class="line">          <span class="keyword">if</span> (snapshot.hasError) &#123;  <span class="comment">// FutureBuilder 已经给我们提供好了 error 状态</span></span><br><span class="line">            <span class="keyword">return</span> _getInfoMessage(snapshot.error);</span><br><span class="line">          &#125;</span><br><span class="line"></span><br><span class="line">          <span class="keyword">if</span> (!snapshot.hasData) &#123; <span class="comment">// FutureBuilder 已经给我们提供好了空数据状态</span></span><br><span class="line">            <span class="keyword">return</span> Center(</span><br><span class="line">              child: CircularProgressIndicator(</span><br><span class="line">                valueColor: AlwaysStoppedAnimation&lt;Color&gt;(Colors.yellow),</span><br><span class="line">                backgroundColor: Colors.yellow[<span class="number">100</span>],</span><br><span class="line">              ),</span><br><span class="line">            );</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">var</span> listData = snapshot.data;</span><br><span class="line">          <span class="keyword">if</span> (listData.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> _getInfoMessage(<span class="string">'No data found'</span>);</span><br><span class="line">          &#125;</span><br><span class="line"></span><br><span class="line">          <span class="keyword">return</span> ListView.builder(</span><br><span class="line">            itemCount: listData.length,</span><br><span class="line">            itemBuilder: (buildContext, index) &#123;</span><br><span class="line">              <span class="keyword">return</span> Column(</span><br><span class="line">                children: &lt;Widget&gt;[</span><br><span class="line">                  ListTile(</span><br><span class="line">                    title: Text(listData[index]),</span><br><span class="line">                  ),</span><br><span class="line">                  Divider(),</span><br><span class="line">                ],</span><br><span class="line">              );</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><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  ...</span><br></pre></td></tr></table></figure><p>通过查看源码，我们可以了解的 <code>FutureBuilder</code> 已经给我处理好了一些基本状态，如图</p><div style="width: 66%; margin:auto"><img src="https://cdn.lishaoy.net/fureBuilderStreamBuilder/snapshot.png" alt="snapshot" title="snapshot"></div><p>我们使用 <code>_getInfoMessage()</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></pre></td><td class="code"><pre><span class="line">Widget _getInfoMessage(String msg) &#123;</span><br><span class="line">  return Center(</span><br><span class="line">    child: Text(msg),</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>就这样我们不使用任何一个 <code>setState()</code> 就能完成和上面一样的效果，并且不会产生副作用，是不是很给力 💪。</p><div class="note info"><p>但是，它并不是完美的，比如，我们想刷新数据，我们需要重新调用 <code>_getListData()</code> 方法，结果它并没有刷新。</p></div>  <h2 id="StreamBuilder"><a href="#StreamBuilder" class="headerlink" title="StreamBuilder"></a>StreamBuilder</h2><p><code>StreamBuilder</code> 通过 <strong>stream:</strong> 参数可以接收一个 <code>stream</code> ，同样，通过 <strong>builder:</strong> 参数来构建 <strong>UI</strong> ，和 <code>futureBuilder</code> 用法类似，唯一的好处就是，我们可以随意控制 <code>stream</code> 的输入输出，添加任何的状态来更新指定状态下的 <strong>UI</strong> 。</p><p>首先，我们使用 <code>enum</code> 来表示我们的状态，在文件的头部添加它，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> StreamViewState &#123; Busy, DataRetrieved, NoData &#125;</span><br></pre></td></tr></table></figure><p>接着，使用 <code>StreamController</code> 创建一个流控制器，把 <code>FutureBuilder</code> 替换成 <code>StreamBuilder</code> ，把 <strong>future:</strong> 参数 改成 <strong>stream:</strong> 参数，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">final</span> StreamController&lt;StreamDemoState&gt; _stateController = StreamController&lt;StreamDemoState&gt;();</span><br><span class="line"></span><br><span class="line"><span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line"></span><br><span class="line">      ...</span><br><span class="line"></span><br><span class="line">      body: StreamBuilder(</span><br><span class="line">        stream: model.homeState,</span><br><span class="line">        builder: (buildContext, snapshot) &#123;</span><br><span class="line">          <span class="keyword">if</span> (snapshot.hasError) &#123;</span><br><span class="line">            <span class="keyword">return</span> _getInfoMessage(snapshot.error);</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="comment">// 使用 枚举的 Busy 来更新数据</span></span><br><span class="line">          <span class="keyword">if</span> (!snapshot.hasData || StreamViewState.Busy) &#123;</span><br><span class="line">            <span class="keyword">return</span> Center(</span><br><span class="line">              child: CircularProgressIndicator(</span><br><span class="line">                valueColor: AlwaysStoppedAnimation&lt;Color&gt;(Colors.yellow),</span><br><span class="line">                backgroundColor: Colors.yellow[<span class="number">100</span>],</span><br><span class="line">              ),</span><br><span class="line">            );</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="comment">//使用 枚举的 NoData 来更新数据</span></span><br><span class="line">          <span class="keyword">if</span> (listItems.length == StreamViewState.NoData) &#123;</span><br><span class="line">            <span class="keyword">return</span> _getInfoMessage(<span class="string">'No data found'</span>);</span><br><span class="line">          &#125;</span><br><span class="line"></span><br><span class="line">          <span class="keyword">return</span> ListView.builder(</span><br><span class="line">            itemCount: listItems.length,</span><br><span class="line">            itemBuilder: (buildContext, index) &#123;</span><br><span class="line">              <span class="keyword">return</span> Column(</span><br><span class="line">                children: &lt;Widget&gt;[</span><br><span class="line">                  ListTile(</span><br><span class="line">                    title: Text(listItems[index]),</span><br><span class="line">                  ),</span><br><span class="line">                  Divider(),</span><br><span class="line">                ],</span><br><span class="line">              );</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><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>只是新增了枚举值来判断是否需要更新数据，其他基本保持不变。</p><p>接下来，我需要修改 <code>_getListData()</code> 方法，使用流控制器添加状态及数据，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">Future _getListData(&#123;<span class="built_in">bool</span> hasError = <span class="keyword">false</span>, <span class="built_in">bool</span> hasData = <span class="keyword">true</span>&#125;) <span class="keyword">async</span> &#123;</span><br><span class="line">  _stateController.add(StreamViewState.Busy);</span><br><span class="line">  <span class="keyword">await</span> Future.delayed(<span class="built_in">Duration</span>(seconds: <span class="number">2</span>));</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (hasError) &#123;</span><br><span class="line">    <span class="keyword">return</span> _stateController.addError(<span class="string">'error'</span>); <span class="comment">// 往 stream 里新增 error 数据</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (!hasData) &#123;</span><br><span class="line">    <span class="keyword">return</span> _stateController.add(StreamViewState.NoData); <span class="comment">// 往 stream 里新增无数据状态</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  _listItems = <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;.generate(<span class="number">10</span>, (index) =&gt; <span class="string">'<span class="subst">$index</span> content'</span>);</span><br><span class="line">  _stateController.add(StreamViewState.DataRetrieved); <span class="comment">// 往 stream 里新增数据获取完成状态</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时我们并没有返回数据，所以我们需要创建 <code>listItems</code> 存储数据，然后把 <code>StatelessWidget</code> 改成 <code>StatefulWidget</code> ，以便我们根据 <code>stream</code> 的输出来更新数据，这个转换非常方便，<strong>VS Code</strong> 编辑器可以使用 <code>Option + Shift + R</code> （Mac）或者 <code>Ctrl + Shift + R</code> (Win)快捷键 ，<strong>Android Studio</strong> 使用<code>Option + Enter</code> 快捷键，之后在 <code>initState()</code> 方法中初始化数据，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="built_in">List</span>&lt;<span class="built_in">String</span>&gt; listItems;</span><br><span class="line"></span><br><span class="line"><span class="meta">@override</span></span><br><span class="line"><span class="keyword">void</span> initState() &#123;</span><br><span class="line">  _getListData();</span><br><span class="line">  <span class="keyword">super</span>.initState();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到这里我们已经解决了 <code>FutureBuilder</code> 的局限性问题，我们可以新增一个 <code>FloatingActionButton</code> 来刷新数据，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(<span class="string">'Stream Builder Demo'</span>),</span><br><span class="line">      ),</span><br><span class="line">      floatingActionButton: FloatingActionButton(</span><br><span class="line">        backgroundColor: Colors.yellow,</span><br><span class="line">        child: Icon(</span><br><span class="line">          Icons.cached,</span><br><span class="line">          color: Colors.black87,</span><br><span class="line">        ),</span><br><span class="line">        onPressed: () &#123;</span><br><span class="line">          model.dispatch(FetchData());</span><br><span class="line">        &#125;,</span><br><span class="line">      ),</span><br><span class="line">      body: StreamBuilder(</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">        </span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>现在，点击 <code>FloatingActionButton</code> 加载指示器已经显示，但是，我们的 <code>listItems</code> 数据并没真正的更新，点击 <code>FloatingActionButton</code> 只是更新的加载状态而已，而且我们的业务逻辑代码和 <strong>UI</strong> 代码还在同一个文件中，很显然，他们已经解耦，所以，我们可以继续完善它，将业务逻辑代码和 <strong>UI</strong> 代码分离出来。</p><h2 id="分离业务逻辑代码和-UI-代码"><a href="#分离业务逻辑代码和-UI-代码" class="headerlink" title="分离业务逻辑代码和 UI 代码"></a>分离业务逻辑代码和 <strong>UI</strong> 代码</h2><p>我们可以把处理 <code>stream</code> 的代码抽离成一个类，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="keyword">import</span> <span class="string">'dart:async'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'dart:math'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:pro_flutter/demo/stream_demo/stream_demo_event.dart'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'package:pro_flutter/demo/stream_demo/stream_demo_state.dart'</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> StreamViewState &#123; Busy, DataRetrieved, NoData &#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StreamDemoModel</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> StreamController&lt;StreamDemoState&gt; _stateController = StreamController&lt;StreamDemoState&gt;();</span><br><span class="line"></span><br><span class="line">  <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt; _listItems;</span><br><span class="line"></span><br><span class="line">  Stream&lt;StreamDemoState&gt; <span class="keyword">get</span> streamState =&gt; _stateController.stream;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">void</span> dispatch(StreamDemoEvent event)&#123;</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">'Event dispatched: <span class="subst">$event<span class="string">');</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    if(event is FetchData) &#123;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">      _getListData(hasData: event.hasData, hasError: event.hasError);</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    &#125;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">  &#125;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string"></span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">  Future _getListData(&#123;bool hasError = false, bool hasData = true&#125;) async &#123;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    _stateController.add(BusyState());</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    await Future.delayed(Duration(seconds: 2));</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string"></span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    if (hasError) &#123;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">      return _stateController.addError('</span></span>error'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!hasData) &#123;</span><br><span class="line">      <span class="keyword">return</span> _stateController.add(DataFetchedState(data: <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    _listItems = <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;.generate(<span class="number">10</span>, (index) =&gt; <span class="string">'<span class="subst">$index</span> content'</span>);</span><br><span class="line">    _stateController.add(DataFetchedState(data: _listItems));</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，把状态也封装成一个文件且将数据和状态关联，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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="class"><span class="keyword">class</span> <span class="title">StreamDemoState</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">InitializedState</span> <span class="keyword">extends</span> <span class="title">StreamDemoState</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DataFetchedState</span> <span class="keyword">extends</span> <span class="title">StreamDemoState</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt; data;</span><br><span class="line"></span><br><span class="line">  DataFetchedState(&#123;<span class="keyword">this</span>.data&#125;);</span><br><span class="line"></span><br><span class="line">  <span class="built_in">bool</span> <span class="keyword">get</span> hasData =&gt; data.length &gt; <span class="number">0</span>;</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">ErrorState</span> <span class="keyword">extends</span> <span class="title">StreamDemoState</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BusyState</span> <span class="keyword">extends</span> <span class="title">StreamDemoState</span></span>&#123;&#125;</span><br></pre></td></tr></table></figure><p>再封装一个事件文件，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="class"><span class="keyword">class</span> <span class="title">StreamDemoEvent</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FetchData</span> <span class="keyword">extends</span> <span class="title">StreamDemoEvent</span></span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">bool</span> hasError;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">bool</span> hasData;</span><br><span class="line"></span><br><span class="line">  FetchData(&#123;<span class="keyword">this</span>.hasError = <span class="keyword">false</span>, <span class="keyword">this</span>.hasData = <span class="keyword">true</span>&#125;);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> toString() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'FetchData &#123; hasError: <span class="subst">$hasError</span>, hasData: <span class="subst">$hasData</span> &#125;'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后，我们 <strong>UI</strong> 部分的代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="class"><span class="keyword">class</span> <span class="title">_StreamBuilderDemoState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">StreamBuilderDemo</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> model = StreamDemoModel(); <span class="comment">// 创建 model</span></span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="keyword">void</span> initState() &#123;</span><br><span class="line">    model.dispatch(FetchData(hasData: <span class="keyword">true</span>)); <span class="comment">// 获取 model 里的数据</span></span><br><span class="line">    <span class="keyword">super</span>.initState();</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">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line"></span><br><span class="line">      ...</span><br><span class="line"></span><br><span class="line">      body: StreamBuilder(</span><br><span class="line">        stream: model.streamState,</span><br><span class="line">        builder: (buildContext, snapshot) &#123;</span><br><span class="line">          <span class="keyword">if</span> (snapshot.hasError) &#123;</span><br><span class="line">            <span class="keyword">return</span> _getInformationMessage(snapshot.error);</span><br><span class="line">          &#125;</span><br><span class="line"></span><br><span class="line">          <span class="keyword">var</span> streamState = snapshot.data;</span><br><span class="line"></span><br><span class="line">          <span class="keyword">if</span> (!snapshot.hasData || streamState <span class="keyword">is</span> BusyState) &#123;  <span class="comment">// 通过封装的状态类来判断是否更新UI</span></span><br><span class="line">            <span class="keyword">return</span> Center(</span><br><span class="line">              child: CircularProgressIndicator(</span><br><span class="line">                valueColor: AlwaysStoppedAnimation&lt;Color&gt;(Colors.yellow),</span><br><span class="line">                backgroundColor: Colors.yellow[<span class="number">100</span>],</span><br><span class="line">              ),</span><br><span class="line">            );</span><br><span class="line">          &#125;</span><br><span class="line"></span><br><span class="line">          <span class="keyword">if</span> (streamState <span class="keyword">is</span> DataFetchedState) &#123; <span class="comment">// 通过封装的状态类来判断是否更新UI</span></span><br><span class="line">            <span class="keyword">if</span> (!homeState.hasData) &#123;</span><br><span class="line">              <span class="keyword">return</span> _getInformationMessage(<span class="string">'not found data'</span>);</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">return</span> ListView.builder(</span><br><span class="line">            itemCount: streamState.data.length,  <span class="comment">// 此时，数据不再是本地数据，而是从 stream 中输出的数据</span></span><br><span class="line">            itemBuilder: (buildContext, index) =&gt;</span><br><span class="line">                _getListItem(index, streamState.data),</span><br><span class="line">          );</span><br><span class="line">        &#125;,</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时，业务逻辑代码和 <strong>UI</strong> 代码已完全分离，且可扩展性和维护增强，且我们的数据和状态已关联起来，此时，点击 <code>FloatingActionButton</code> 效果和上面一样，且数据已更新。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/futrueBuilder-streamBuilder.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Android Flutter 混合开发高仿大厂App</title>
      <link>https://h.lishaoy.net/androidCtrip.html</link>
      <guid>https://h.lishaoy.net/androidCtrip.html</guid>
      <pubDate>Thu, 18 Jun 2020 22:36:43 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/ctrip/android/android_ctrip_h.png&quot; class=&quot;full-image&quot; alt=&quot;Flutter&quot; title=&quot;Flutter&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;自上篇 &lt;a href=&quot;https://h.lishaoy.net/flutterctrip&quot;&gt;Flutter 10天高仿大厂App及小技巧积累总结&lt;/a&gt; 的续篇，这次更是干货满满。&lt;/p&gt;
&lt;p&gt;这篇文章将概述 &lt;strong&gt;Android组件化的架构搭建&lt;/strong&gt; 及 &lt;strong&gt;Flutter&lt;/strong&gt; 和 &lt;strong&gt;Android&lt;/strong&gt; 如何混合开发 &lt;em&gt;(整个App只有首页是用原生Android完成，其他页面都是引入之前的做好的Flutter页面)&lt;/em&gt; ，主宿主程序由 Android 搭建，采用了组件化的架构搭建整个 &lt;strong&gt;App&lt;/strong&gt; ，不同业务，对应不同的 module 工程，业务之间采用接口通信 &lt;em&gt;(ARouter)&lt;/em&gt; ，以 module 的形式混入 Flutter，通过 &lt;strong&gt;MethodChannel&lt;/strong&gt; 和 &lt;strong&gt;Flutter&lt;/strong&gt; 端进行数据通信等，且这些功能实现源码开源，感兴趣的小伙伴可以移步至 &lt;a href=&quot;https://github.com/persilee/android_ctrip&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/ctrip/android/android_ctrip_h.png" class="full-image" alt="Flutter" title="Flutter"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>自上篇 <a href="https://h.lishaoy.net/flutterctrip">Flutter 10天高仿大厂App及小技巧积累总结</a> 的续篇，这次更是干货满满。</p><p>这篇文章将概述 <strong>Android组件化的架构搭建</strong> 及 <strong>Flutter</strong> 和 <strong>Android</strong> 如何混合开发 <em>(整个App只有首页是用原生Android完成，其他页面都是引入之前的做好的Flutter页面)</em> ，主宿主程序由 Android 搭建，采用了组件化的架构搭建整个 <strong>App</strong> ，不同业务，对应不同的 module 工程，业务之间采用接口通信 <em>(ARouter)</em> ，以 module 的形式混入 Flutter，通过 <strong>MethodChannel</strong> 和 <strong>Flutter</strong> 端进行数据通信等，且这些功能实现源码开源，感兴趣的小伙伴可以移步至 <a href="https://github.com/persilee/android_ctrip" target="_blank" rel="noopener">GitHub</a>。</p><hr><a id="more"></a><p>以下博文会分为4个部分概述：</p><ul><li>项目完成的功能预览</li><li>项目组件化结构分析</li><li>项目功能详细概述（所用知识点）</li><li>Android Flutter 混合开发</li></ul><h2 id="项目完成的功能预览"><a href="#项目完成的功能预览" class="headerlink" title="项目完成的功能预览"></a>项目完成的功能预览</h2><p>首先，我们还是通过一个视频来快速预览下项目完成的功能和运行效果，如下</p><video id="flutter" class="video-js vjs-default-skin" controls preload="auto" poster="https://cdn.lishaoy.net/ctrip/android_ctripb_bg.png" data-setup="{'example_option':true}">    <source src="https://cdn.lishaoy.net/ctrip/android_ctrip.mp4" type="video/mp4"></video><div class="note warning"><p><i class="fa fa-fw fa-bell  faa-horizontal animated faa-slow" style="color: #faab33;"></i> 如视频播放失败， <a href="https://www.bilibili.com/video/BV1W54y1B72U/" target="_blank" rel="noopener">请移步这里点击观看</a> <em>(点击齿轮 –&gt; 更多播放设置，可以隐藏黑边)</em></p></div><p>看完视频后，其实大部分功能和之前的 <a href="https://h.lishaoy.net/flutterctrip">纯flutter项目</a> 功能相同，只是首页新增了4个tab推荐页面及携程二楼和布局改变。</p><p>大家也可扫描，安装体验：</p><div style="width:166px; margin:auto"><img src="https://www.pgyer.com/app/qrcode/AsHK?sign=&amp;auSign=&amp;code=" alt="no-shadow" title="手机扫描二维码安装"></div><h2 id="项目组件化结构分析"><a href="#项目组件化结构分析" class="headerlink" title="项目组件化结构分析"></a>项目组件化结构分析</h2><h3 id="项目结构图预览"><a href="#项目结构图预览" class="headerlink" title="项目结构图预览"></a>项目结构图预览</h3><p>其次，分析梳理下项目结构，项目的结构大致如图，还有一些细枝末节的没有体现在图里：</p><p><img src="https://cdn.lishaoy.net/ctrip/android/project.png" alt="no-shadow" title="project structure"></p><h3 id="项目结构分析"><a href="#项目结构分析" class="headerlink" title="项目结构分析"></a>项目结构分析</h3><h4 id="业务工程"><a href="#业务工程" class="headerlink" title="业务工程"></a>业务工程</h4><p>把具体独立的业务都拆分成单独的 module 减小项目的维护压力</p><ul><li>ft_home: 首页模块，这个模块其实还可以继续拆分，可把4个 tab <em>(精选、附近、景点、美食)</em> 页都拆成模块，这里我暂时没有拆分，后续会完成</li><li>ft_destination: 目的地模块，其实并没有建立这个模块，因为直接引入了之前做好的 flutter 页面</li><li>ft_travel: 旅拍模块，同样也使用了 flutter 页面</li><li>flutter: flutter模块，这个模块是从 flutter_module 中自动生成的，后面介绍到</li></ul><h3 id="基础库工程"><a href="#基础库工程" class="headerlink" title="基础库工程"></a>基础库工程</h3><p>把具体的功能都封装成独立的库供业务模块使用，降低项目的维护成本及代码之间耦合性</p><ul><li>lib_network: 网络库，使用 <a href="https://github.com/square/okhttp" target="_blank" rel="noopener">okhttp</a> 插件二次封装，业务层简单的调用即可</li><li>lib_webview: 打开网页的webview库，使用了 <a href="https://github.com/Justson/AgentWeb" target="_blank" rel="noopener">agentweb</a> 插件二次封装，业务层只需要一句代码即可完成网页的跳转</li><li>lib_image_loader: 图片加载库，使用了 <a href="https://github.com/bumptech/glide" target="_blank" rel="noopener">glide</a> 插件二次封装，业务层只需一句代码即可加载不同参数的图片</li><li>lib_asr: 百度AI语音库，通过 Android 集成好供 Flutter 端使用</li><li>lib_common_ui: 公共UI库，重复多次使用的页面集中管理</li><li>lib_base: 基础库，通过 <a href="https://github.com/alibaba/ARouter" target="_blank" rel="noopener">ARouter</a> 的 service 功能暴露接口提供服务给业务层，当然业务层也可以在这里暴露接口供外界使用</li></ul><p>这里有一些使用的插件并没有在项目结构图里体现出来(结构图空间有限)。</p><h3 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h3><p>在这里把项目使用的插件整理列举出来供大家参考：</p><ul><li><a href="https://github.com/hackware1993/MagicIndicator" target="_blank" rel="noopener">magicindicator</a> 强大、可定制、易扩展的 ViewPager 指示器框架，首页的4个 tab <em>(精选、附近、景点、美食)</em> 就是用这个实现的。</li><li><a href="https://github.com/gyf-dev/ImmersionBar" target="_blank" rel="noopener">immersionbar</a> 一句代码轻松实现状态栏、导航栏沉浸式管理</li><li><a href="https://github.com/tyzlmjj/PagerBottomTabStrip" target="_blank" rel="noopener">pagerBottomTabStrip</a> 页面底部和侧边的导航栏，首页、目的地、旅拍、我的页面切换就是用这个实现的。</li><li><a href="https://github.com/ReactiveX/RxAndroid" target="_blank" rel="noopener">rxjava/rxandroid</a> 异步和链式编程</li><li><a href="https://github.com/JakeWharton/butterknife" target="_blank" rel="noopener">butterknife</a> view注入插件，配合Android插件使用，可快速自动生成 init view的代码，不用写一句 <code>findViewById</code> 的代码。</li><li><a href="https://github.com/google/gson" target="_blank" rel="noopener">gson</a> json解析，配合Android插件使用，可快速生成实体类</li><li><a href="https://github.com/scwang90/SmartRefreshLayout" target="_blank" rel="noopener">smartRefreshLayout</a> 智能下拉刷新框架，携程二楼及下拉刷新加载更多就是用这个实现的</li><li><a href="https://github.com/greenrobot/EventBus" target="_blank" rel="noopener">eventbus</a> 发布/订阅事件总线，优雅的完成组件之间通信</li><li><a href="https://github.com/alibaba/ARouter" target="_blank" rel="noopener">arouter</a> 依赖注入、路由跳转、注册service，优雅的完成模块之间的通信</li><li><a href="https://github.com/square/okhttp" target="_blank" rel="noopener">okhttp</a> 网络请求插件</li><li><a href="https://github.com/Justson/AgentWeb" target="_blank" rel="noopener">agentweb</a> webview框架，进行简单的二次封装可优雅的进行网页跳转</li><li><a href="https://github.com/bumptech/glide" target="_blank" rel="noopener">glide</a> 高性能、可扩展的图片加载插件</li><li><a href="https://github.com/youth5201314/banner" target="_blank" rel="noopener">banner</a> 图片轮播控件</li></ul><p>基本就是这些了，应该没有漏的，插件的详细使用，请进入各插件的 GitHub 主页。</p><p>在此，把我项目的插件引入代码及版本管理的 <code>gradle</code> 代码贴出来，如下：</p><p>插件引入代码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">dependencies &#123;</span><br><span class="line">    implementation fileTree(dir: <span class="string">'libs'</span>, include: [<span class="string">'*.jar'</span>])</span><br><span class="line"></span><br><span class="line">    implementation rootProject.depsLibs.appcompat</span><br><span class="line">    implementation rootProject.depsLibs.legacy</span><br><span class="line">    implementation rootProject.depsLibs.recyclerview</span><br><span class="line">    implementation rootProject.depsLibs.constraintlayout</span><br><span class="line">    implementation rootProject.depsLibs.cardview</span><br><span class="line"></span><br><span class="line">    //tab指示器</span><br><span class="line">    implementation rootProject.depsLibs.magicindicator</span><br><span class="line">    //沉浸式</span><br><span class="line">    implementation rootProject.depsLibs.immersionbar</span><br><span class="line">    //导航栏</span><br><span class="line">    implementation rootProject.depsLibs.pagerBottomTabStrip</span><br><span class="line">    //rxjava</span><br><span class="line">    implementation rootProject.depsLibs.rxjava</span><br><span class="line">    //rxandroid</span><br><span class="line">    implementation rootProject.depsLibs.rxandroid</span><br><span class="line">    //view 注入</span><br><span class="line">    implementation rootProject.depsLibs.butterknife</span><br><span class="line">    //view 注入</span><br><span class="line">    annotationProcessor rootProject.depsLibs.butterknifeCompiler</span><br><span class="line">    //gson</span><br><span class="line">    implementation rootProject.depsLibs.gson</span><br><span class="line">    //banner</span><br><span class="line">    implementation rootProject.depsLibs.banner</span><br><span class="line">    //smartRefreshLayout 上下拉刷新</span><br><span class="line">    implementation rootProject.depsLibs.smartRefreshLayout</span><br><span class="line">    implementation rootProject.depsLibs.refreshHeader</span><br><span class="line">    implementation rootProject.depsLibs.refreshHeaderTwoLevel</span><br><span class="line">    implementation rootProject.depsLibs.refreshFooter</span><br><span class="line">    //eventbus</span><br><span class="line">    implementation rootProject.depsLibs.eventbus</span><br><span class="line">    //arouter库</span><br><span class="line">    implementation(rootProject.depsLibs.arouterapi) &#123;</span><br><span class="line">        exclude group: <span class="string">'com.android.support'</span></span><br><span class="line">    &#125;</span><br><span class="line">    annotationProcessor rootProject.depsLibs.aroutercompiler</span><br><span class="line"></span><br><span class="line">    //引入home模块</span><br><span class="line">    implementation project(<span class="string">':ft_home'</span>)</span><br><span class="line">    //引入图片加载库</span><br><span class="line">    implementation project(<span class="string">':lib_image_loader'</span>)</span><br><span class="line">    //引入网络库</span><br><span class="line">    implementation project(<span class="string">':lib_network'</span>)</span><br><span class="line">    //webview</span><br><span class="line">    implementation project(<span class="string">':lib_webview'</span>)</span><br><span class="line">    //引入基础ui库</span><br><span class="line">    implementation project(<span class="string">':lib_common_ui'</span>)</span><br><span class="line">    //base库</span><br><span class="line">    implementation project(<span class="string">':lib_base'</span>)</span><br><span class="line">    //引入flutter模块</span><br><span class="line">    implementation project(<span class="string">':flutter'</span>)</span><br><span class="line">    //引入百度AI语音库</span><br><span class="line">    implementation project(<span class="string">':lib_asr'</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>版本管理代码 <em>(统一管理版本号)</em> : </p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line">ext &#123;</span><br><span class="line">    android = [</span><br><span class="line">            compileSdkVersion: 29,</span><br><span class="line">            buildToolsVersion: <span class="string">"29.0.0"</span>,</span><br><span class="line">            minSdkVersion    : 19,</span><br><span class="line">            targetSdkVersion : 29,</span><br><span class="line">            applicationId    : <span class="string">'net.lishaoy.android_ctrip'</span>,</span><br><span class="line">            versionCode      : 1,</span><br><span class="line">            versionName      : <span class="string">'1.0'</span>,</span><br><span class="line">            multiDexEnabled  : <span class="literal">true</span>,</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    depsVersion = [</span><br><span class="line">            appcompat            : <span class="string">'1.1.0'</span>,</span><br><span class="line">            legacy               : <span class="string">'1.0.0'</span>,</span><br><span class="line">            recyclerview         : <span class="string">'1.0.0'</span>,</span><br><span class="line">            constraintlayout     : <span class="string">'1.1.3'</span>,</span><br><span class="line">            cardview             : <span class="string">'1.0.0'</span>,</span><br><span class="line">            magicindicator       : <span class="string">'1.5.0'</span>,</span><br><span class="line">            immersionbar         : <span class="string">'3.0.0'</span>,</span><br><span class="line">            pagerBottomTabStrip  : <span class="string">'2.3.0X'</span>,</span><br><span class="line">            glide                : <span class="string">'4.11.0'</span>,</span><br><span class="line">            glidecompiler        : <span class="string">'4.11.0'</span>,</span><br><span class="line">            butterknife          : <span class="string">'10.2.1'</span>,</span><br><span class="line">            butterknifeCompiler  : <span class="string">'10.2.1'</span>,</span><br><span class="line">            rxjava               : <span class="string">'3.0.0'</span>,</span><br><span class="line">            rxandroid            : <span class="string">'3.0.0'</span>,</span><br><span class="line">            okhttp               : <span class="string">'4.7.2'</span>,</span><br><span class="line">            okhttpLogging        : <span class="string">'4.7.2'</span>,</span><br><span class="line">            gson                 : <span class="string">'2.8.6'</span>,</span><br><span class="line">            banner               : <span class="string">'2.0.10'</span>,</span><br><span class="line">            smartRefreshLayout   : <span class="string">'2.0.1'</span>,</span><br><span class="line">            refreshHeader        : <span class="string">'2.0.1'</span>,</span><br><span class="line">            refreshFooter        : <span class="string">'2.0.1'</span>,</span><br><span class="line">            refreshHeaderTwoLevel: <span class="string">'2.0.1'</span>,</span><br><span class="line">            eventbus             : <span class="string">'3.2.0'</span>,</span><br><span class="line">            agentweb             : <span class="string">'4.1.3'</span>,</span><br><span class="line">            arouterapi           : <span class="string">'1.5.0'</span>,</span><br><span class="line">            aroutercompiler      : <span class="string">'1.2.2'</span>,</span><br><span class="line"></span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line">    depsLibs = [</span><br><span class="line">            appcompat            : <span class="string">"androidx.appcompat:appcompat:<span class="variable">$&#123;depsVersion.appcompat&#125;</span>"</span>,</span><br><span class="line">            legacy               : <span class="string">"androidx.legacy:legacy-support-v4:<span class="variable">$&#123;depsVersion.legacy&#125;</span>"</span>,</span><br><span class="line">            recyclerview         : <span class="string">"androidx.recyclerview:recyclerview:<span class="variable">$&#123;depsVersion.recyclerview&#125;</span>"</span>,</span><br><span class="line">            constraintlayout     : <span class="string">"androidx.constraintlayout:constraintlayout:<span class="variable">$&#123;depsVersion.constraintlayout&#125;</span>"</span>,</span><br><span class="line">            cardview             : <span class="string">"androidx.cardview:cardview:<span class="variable">$&#123;depsVersion.cardview&#125;</span>"</span>,</span><br><span class="line">            magicindicator       : <span class="string">"com.github.hackware1993:MagicIndicator:<span class="variable">$&#123;depsVersion.magicindicator&#125;</span>"</span>,</span><br><span class="line">            immersionbar         : <span class="string">"com.gyf.immersionbar:immersionbar:<span class="variable">$&#123;depsVersion.immersionbar&#125;</span>"</span>,</span><br><span class="line">            pagerBottomTabStrip  : <span class="string">"me.majiajie:pager-bottom-tab-strip:<span class="variable">$&#123;depsVersion.pagerBottomTabStrip&#125;</span>"</span>,</span><br><span class="line">            glide                : <span class="string">"com.github.bumptech.glide:glide:<span class="variable">$&#123;depsVersion.glide&#125;</span>"</span>,</span><br><span class="line">            glidecompiler        : <span class="string">"com.github.bumptech.glide:compiler:<span class="variable">$&#123;depsVersion.glidecompiler&#125;</span>"</span>,</span><br><span class="line">            butterknife          : <span class="string">"com.jakewharton:butterknife:<span class="variable">$&#123;depsVersion.butterknife&#125;</span>"</span>,</span><br><span class="line">            butterknifeCompiler  : <span class="string">"com.jakewharton:butterknife-compiler:<span class="variable">$&#123;depsVersion.butterknifeCompiler&#125;</span>"</span>,</span><br><span class="line">            rxjava               : <span class="string">"io.reactivex.rxjava3:rxjava:<span class="variable">$&#123;depsVersion.rxjava&#125;</span>"</span>,</span><br><span class="line">            rxandroid            : <span class="string">"io.reactivex.rxjava3:rxandroid:<span class="variable">$&#123;depsVersion.rxandroid&#125;</span>"</span>,</span><br><span class="line">            okhttp               : <span class="string">"com.squareup.okhttp3:okhttp:<span class="variable">$&#123;depsVersion.okhttp&#125;</span>"</span>,</span><br><span class="line">            okhttpLogging        : <span class="string">"com.squareup.okhttp3:logging-interceptor:<span class="variable">$&#123;depsVersion.okhttpLogging&#125;</span>"</span>,</span><br><span class="line">            gson                 : <span class="string">"com.google.code.gson:gson:<span class="variable">$&#123;depsVersion.gson&#125;</span>"</span>,</span><br><span class="line">            banner               : <span class="string">"com.youth.banner:banner:<span class="variable">$&#123;depsVersion.banner&#125;</span>"</span>,</span><br><span class="line">            smartRefreshLayout   : <span class="string">"com.scwang.smart:refresh-layout-kernel:<span class="variable">$&#123;depsVersion.smartRefreshLayout&#125;</span>"</span>,</span><br><span class="line">            refreshHeader        : <span class="string">"com.scwang.smart:refresh-header-classics:<span class="variable">$&#123;depsVersion.refreshHeader&#125;</span>"</span>,</span><br><span class="line">            refreshHeaderTwoLevel: <span class="string">"com.scwang.smart:refresh-header-two-level:<span class="variable">$&#123;depsVersion.refreshHeader&#125;</span>"</span>,</span><br><span class="line">            refreshFooter        : <span class="string">"com.scwang.smart:refresh-footer-classics:<span class="variable">$&#123;depsVersion.refreshFooter&#125;</span>"</span>,</span><br><span class="line">            eventbus             : <span class="string">"org.greenrobot:eventbus:<span class="variable">$&#123;depsVersion.eventbus&#125;</span>"</span>,</span><br><span class="line">            agentweb             : <span class="string">"com.just.agentweb:agentweb:<span class="variable">$&#123;depsVersion.agentweb&#125;</span>"</span>,</span><br><span class="line">            arouterapi           : <span class="string">"com.alibaba:arouter-api:<span class="variable">$&#123;depsVersion.arouterapi&#125;</span>"</span>,</span><br><span class="line">            aroutercompiler      : <span class="string">"com.alibaba:arouter-compiler:<span class="variable">$&#123;depsVersion.aroutercompiler&#125;</span>"</span>,</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="项目功能详细概述（所用知识点）"><a href="#项目功能详细概述（所用知识点）" class="headerlink" title="项目功能详细概述（所用知识点）"></a>项目功能详细概述（所用知识点）</h2><p>这里主要对首页功能及知识点进行概述，由于其他页面是引入了之前的 Flutter 页面， 具体功能在 <a href="https://h.lishaoy.net/flutterctrip">Flutter 10天高仿大厂App及小技巧积累总结</a> 已经介绍过了，在这就不再阐述。</p><p>首页重点概述以下功能的实现：</p><ul><li>下拉刷新、携程二楼</li><li>搜索appBar</li><li>渐变色网格导航</li><li>banner组件</li><li>多状态的tab指示器 <em>(滚动固定顶部)</em></li></ul><h3 id="下拉刷新、携程二楼"><a href="#下拉刷新、携程二楼" class="headerlink" title="下拉刷新、携程二楼"></a>下拉刷新、携程二楼</h3><p>首先，看看具体的效果图，如图：</p><p><img src="https://cdn.lishaoy.net/ctrip/android/second_floor.gif" alt="no-shadow" title="second floor"></p><p>下拉刷新和携程二楼是使用 <a href="https://github.com/scwang90/SmartRefreshLayout" target="_blank" rel="noopener">smartRefreshLayout</a> 插件完成的，实现代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></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">initRefreshMore</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    homeHeader.setRefreshHeader(<span class="keyword">new</span> ClassicsHeader(getContext()), -<span class="number">1</span>, (<span class="keyword">int</span>) Utils.dp2px(<span class="number">76</span>)); <span class="comment">//设置下拉刷新及二楼header的高度</span></span><br><span class="line">    homeHeader.setFloorRate(<span class="number">1.6f</span>); <span class="comment">//设置二楼触发比率</span></span><br><span class="line">    homeRefreshContainer.setPrimaryColorsId(R.color.colorPrimary, R.color.white); <span class="comment">//设置下拉刷新及二楼提示文字颜色</span></span><br><span class="line">    homeRefreshContainer.setOnMultiListener(<span class="keyword">new</span> SimpleMultiListener() &#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">onLoadMore</span><span class="params">(@NonNull RefreshLayout refreshLayout)</span> </span>&#123;</span><br><span class="line">            loadMore(refreshLayout); <span class="comment">//加载更多</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">onRefresh</span><span class="params">(@NonNull RefreshLayout refreshLayout)</span> </span>&#123;</span><br><span class="line">            refreshLayout.finishRefresh(<span class="number">1600</span>); <span class="comment">//设置下拉刷新延迟</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">onHeaderMoving</span><span class="params">(RefreshHeader header, <span class="keyword">boolean</span> isDragging, <span class="keyword">float</span> percent, <span class="keyword">int</span> offset, <span class="keyword">int</span> headerHeight, <span class="keyword">int</span> maxDragHeight)</span> </span>&#123;</span><br><span class="line">            homeSecondFloorImg.setVisibility(View.VISIBLE);  <span class="comment">//隐藏二楼背景图</span></span><br><span class="line">            homeSearchBarContainer.setAlpha(<span class="number">1</span> - Math.min(percent, <span class="number">1</span>)); <span class="comment">//改变searchBar透明度</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">onStateChanged</span><span class="params">(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (oldState == RefreshState.ReleaseToTwoLevel) &#123;  <span class="comment">//即将去往二楼状态处理</span></span><br><span class="line">                homeSecondFloorImg.setVisibility(View.GONE);</span><br><span class="line">                homeHeaderContent.animate().alpha(<span class="number">1</span>).setDuration(<span class="number">666</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (newState == RefreshState.PullDownCanceled) &#123; <span class="comment">//下拉取消状态处理</span></span><br><span class="line">                homeHeaderContent.animate().alpha(<span class="number">0</span>).setDuration(<span class="number">666</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (newState == RefreshState.Refreshing) &#123; <span class="comment">//正在刷新状态处理</span></span><br><span class="line">                homeHeaderContent.animate().alpha(<span class="number">0</span>).setDuration(<span class="number">666</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (oldState == RefreshState.TwoLevelReleased) &#123; <span class="comment">// 准备去往二楼完成状态处理，这里打开webview</span></span><br><span class="line">                WebViewImpl.getInstance().gotoWebView(<span class="string">"https://m.ctrip.com/webapp/you/tsnap/secondFloorIndex.html?isHideNavBar=YES&amp;s_guid=feb780be-c55a-4f92-a6cd-2d81e04d3241"</span>, <span class="keyword">true</span>);</span><br><span class="line">                homeHeader.finishTwoLevel();</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (oldState == RefreshState.TwoLevel) &#123; <span class="comment">//到达二楼状态处理</span></span><br><span class="line">                homeCustomScrollView.setVisibility(View.GONE);</span><br><span class="line">                homeHeaderContent.animate().alpha(<span class="number">0</span>).setDuration(<span class="number">666</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (oldState == RefreshState.TwoLevelFinish) &#123; <span class="comment">//二楼完成状态处理</span></span><br><span class="line">                homeCustomScrollView.setVisibility(View.VISIBLE);</span><br><span class="line">                homeCustomScrollView.animate().alpha(<span class="number">1</span>).setDuration(<span class="number">666</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>XML</code> 页面布局文件代码如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="tag">&lt;<span class="name">com.scwang.smart.refresh.layout.SmartRefreshLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">"@+id/home_refresh_container"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:clipChildren</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">app:srlAccentColor</span>=<span class="string">"@color/colorPrimary"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">app:srlPrimaryColor</span>=<span class="string">"@color/colorPrimary"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">com.scwang.smart.refresh.header.TwoLevelHeader</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">"@+id/home_header"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:gravity</span>=<span class="string">"top"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">ImageView</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">"@+id/home_second_floor_img"</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">"460dp"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_alignTop</span>=<span class="string">"@+id/home_header"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:scaleType</span>=<span class="string">"fitXY"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:src</span>=<span class="string">"@drawable/second_floor"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:visibility</span>=<span class="string">"gone"</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">FrameLayout</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">"@+id/home_header_content"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:alpha</span>=<span class="string">"0"</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">ImageView</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:scaleType</span>=<span class="string">"fitXY"</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:src</span>=<span class="string">"@drawable/second_floor"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">FrameLayout</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">com.scwang.smart.refresh.header.TwoLevelHeader</span>&gt;</span></span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">com.scwang.smart.refresh.footer.ClassicsFooter</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">com.scwang.smart.refresh.layout.SmartRefreshLayout</span>&gt;</span></span><br></pre></td></tr></table></figure><p>具体实现详情，可移步 <a href="https://github.com/persilee/android_ctrip" target="_blank" rel="noopener">GitHub</a> 查看源码。</p><h3 id="搜索appBar"><a href="#搜索appBar" class="headerlink" title="搜索appBar"></a>搜索appBar</h3><p>搜索栏的滚动的 placeholder 文字是使用 <a href="https://github.com/youth5201314/banner" target="_blank" rel="noopener">banner</a> 插件实现的，点击搜索框可跳转到搜索页面 <em>(flutter写的搜索页面)</em> ，跳转页面后可以把 placeholder 文字带到 flutter 搜索页面。</p><p>效果如图：</p><p><img src="https://cdn.lishaoy.net/ctrip/android/searchBar.gif" alt="no-shadow" title="search bar"></p><p>滚动的placeholder文字实现代码如下 <em>(搜索框的实现就不再这里展示都是一些XML布局代码)</em>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">    homeSearchBarPlaceholder</span><br><span class="line">            .setAdapter(<span class="keyword">new</span> HomeSearchBarPlaceHolderAdapter(homeData.getSearchPlaceHolderList())) <span class="comment">// 设置适配器</span></span><br><span class="line">            .setOrientation(Banner.VERTICAL) <span class="comment">// 设置滚动方向</span></span><br><span class="line">            .setDelayTime(<span class="number">3600</span>) <span class="comment">// 设置间隔时间</span></span><br><span class="line">            .setOnBannerListener(<span class="keyword">new</span> OnBannerListener() &#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">OnBannerClick</span><span class="params">(Object data, <span class="keyword">int</span> position)</span> </span>&#123;  <span class="comment">//点击打开 flutter 搜索页面</span></span><br><span class="line">                    ARouter.getInstance()</span><br><span class="line">                            .build(<span class="string">"/home/search"</span>)</span><br><span class="line">                            .withString(<span class="string">"placeHolder"</span>, ((Home.SearchPlaceHolderListBean) data).getText())</span><br><span class="line">                            .navigation();</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>searchBar的具体功能不过多阐述，和之前的项目一致。</p><h3 id="渐变色网格导航"><a href="#渐变色网格导航" class="headerlink" title="渐变色网格导航"></a>渐变色网格导航</h3><p>渐变色网格导航基本都是一些 <code>XML</code> 页面布局代码，只是我把它封装成了单独的组件，效果如图</p><p><img src="https://cdn.lishaoy.net/ctrip/android/gridBar.png" alt="GridNav" width="36%" title="GridNav" align="center"></p><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><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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="tag">&lt;<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:orientation</span>=<span class="string">"vertical"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:background</span>=<span class="string">"@color/white"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 网格导航 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">net.lishaoy.ft_home.GridNavView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">"@+id/home_grid_nav_container"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    ...</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><p>具体实现详情，可移步 <a href="https://github.com/persilee/android_ctrip" target="_blank" rel="noopener">GitHub</a> 查看源码。</p><h3 id="banner组件"><a href="#banner组件" class="headerlink" title="banner组件"></a>banner组件</h3><p>banner组件也是用 <a href="https://github.com/youth5201314/banner" target="_blank" rel="noopener">banner</a> 插件实现的，如图</p><p><img src="https://cdn.lishaoy.net/ctrip/android/banner.gif" alt="no-shadow" title="banner"></p><p>实现代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initBanner</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    homeBanner.addBannerLifecycleObserver(<span class="keyword">this</span>)</span><br><span class="line">            .setAdapter(<span class="keyword">new</span> HomeBannerAdapter(homeData.getBannerList())) <span class="comment">//设置适配器</span></span><br><span class="line">            .setIndicator(<span class="keyword">new</span> EllipseIndicator(getContext()))           <span class="comment">//设置指示器，如图的指示器是我自定义的插件里并没有提供</span></span><br><span class="line">            .setIndicatorSelectedColorRes(R.color.white)                <span class="comment">//设置指示器颜色</span></span><br><span class="line">            .setIndicatorSpace((<span class="keyword">int</span>) BannerUtils.dp2px(<span class="number">10</span>))             <span class="comment">//设置间距</span></span><br><span class="line">            .setBannerRound(BannerUtils.dp2px(<span class="number">6</span>));                      <span class="comment">//设置圆角</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="多状态的tab指示器"><a href="#多状态的tab指示器" class="headerlink" title="多状态的tab指示器"></a>多状态的tab指示器</h3><p>多状态的tab指示器的实现需要注意很多细节，因为它是在首页的 <code>fragment</code> 的 <code>ScrollView</code> 里嵌入 <code>viewPaper</code>，首先你会发现 viewPaper 不显示的问题，其次是滚动不流畅的问题，这两个问题我的解决方案是：</p><ul><li>viewPaper 不显示的问题：使用自定义的 <code>ViewPager</code> 重写 <code>onMeasure</code> 方法，重新计算高度</li><li>滚动不流畅的问题：使用自定义的 <code>ScrollView</code>，重写 <code>computeScroll</code> 和 <code>onScrollChanged</code> 重新获取滚动距离</li></ul><p>实现效果如图：</p><p><img src="https://cdn.lishaoy.net/ctrip/android/tab_bar.gif" alt="no-shadow" title="tab page"></p><p>这个功能实现代码过多不便在这里展示，具体实现详情，可移步 <a href="https://github.com/persilee/android_ctrip" target="_blank" rel="noopener">GitHub</a> 查看源码。</p><h2 id="Android-Flutter-混合开发"><a href="#Android-Flutter-混合开发" class="headerlink" title="Android Flutter 混合开发"></a>Android Flutter 混合开发</h2><p>这个项目的实现只有首页是用 Android 原生实现，其他的页面均是 Flutter 实现的，之前 <a href="https://h.lishaoy.net/flutterctrip">纯Flutter项目</a>。</p><p>Android 引入 Flutter 进行混合开发，需要以下几个步骤</p><ul><li>建立一个flutter module</li><li>编写flutter代码 <em>(创建 flutter 路由)</em></li><li>flutter 和 android 之间相互通信</li></ul><p>下面依次概述这几部分是如何操作实现的。</p><h3 id="建立一个flutter-module"><a href="#建立一个flutter-module" class="headerlink" title="建立一个flutter module"></a>建立一个flutter module</h3><p>这个应该不用过多描述，基本操作大家都会 File –&gt; New –&gt; New Module 如图：</p><p><img src="https://cdn.lishaoy.net/ctrip/android/flutter_module.png" alt="no-shadow" title="flutter module"></p><p>新建完成之后，android studio 会自动生成配置代码到 gradle 配置文件里，且生成一个 flutter 的 library 模块。</p><div class="note warning"><p><i class="fa fa-fw fa-bell  faa-horizontal animated faa-slow" style="color: #faab33;"></i> <strong>Tips：</strong> <br \="">新建的时候最好 flutter module 和 android 项目放到同级目录下；<br \="">新版的 android studio 才会自动生成 gradle 配置代码，老版本貌似需要手动配置</p></div><p>如，没有生成 gradle 配置代码，你需要在根项目的 <code>settings.gradle</code> 文件里手动加入如下配置：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">setBinding(<span class="keyword">new</span> Binding([gradle: <span class="keyword">this</span>]))</span><br><span class="line">evaluate(<span class="keyword">new</span> File(</span><br><span class="line">  settingsDir, <span class="comment">//设置根路径，根据具体flutter module路径配置</span></span><br><span class="line">  <span class="string">'flutter_module/.android/include_flutter.groovy'</span></span><br><span class="line">))</span><br><span class="line"></span><br><span class="line">include <span class="string">':flutter_module'</span></span><br></pre></td></tr></table></figure><p>还需在宿主工程 <em>(没改名的话都是app)</em> 的 <code>build.gradle</code> 引入 flutter， 如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">dependencies &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="comment">//引入flutter模块</span></span><br><span class="line">    <span class="function">implementation <span class="title">project</span><span class="params">(<span class="string">':flutter'</span>)</span></span></span><br><span class="line"><span class="function">    ...</span></span><br><span class="line"><span class="function">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="编写flutter代码"><a href="#编写flutter代码" class="headerlink" title="编写flutter代码"></a>编写flutter代码</h3><p>编写flutter代码，在 flutter module 里按照正常 flutter 开发流程编写 flutter 代码即可。 <em>(我项目里的 flutter 的代码是之前项目都写好的，复制过来，改改包的引入问题，就可以运行了。)</em></p><p>这里需要注意的是，flutter 有且只有一个入口，就是 <code>main()</code> 函数，我们需要在这里处理好 flutter 页面的跳转问题。</p><p>在 android 端，创建 flutter 页面，代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Flutter.createView(getActivity(),getLifecycle(),<span class="string">"destination"</span>);</span><br></pre></td></tr></table></figure><p><code>Flutter.createView</code> 需要3个参数 <code>activity</code> 、<code>lifecycle</code> 、<code>route</code> ，这个 route 就是要传递到 flutter 端的，当然，它是 String 类型的，我们可以自由发挥传递普通字符串或 json 字符串等。</p><p>我们也可以通过其他的方式创建 flutter 页面，如： <code>Flutter.createFragment()</code> 、 <code>FlutterActivity.withNewEngine()</code>、 <code>FlutterFragment.createDefault()</code> 等。</p><p>具体的使用，可前往 <a href="https://flutter.dev/docs/development/add-to-app/android" target="_blank" rel="noopener">Flutter官方文档</a> 查阅。</p><p>那么，flutter 端如何接收这个 route 参数，是通过 <code>window.defaultRouteName</code>，此项目里管理 flutter 端路由代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> main() =&gt; runApp(MyApp());</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyApp</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> MaterialApp(</span><br><span class="line">      debugShowCheckedModeBanner: <span class="keyword">false</span>,</span><br><span class="line">      title: <span class="string">'Flutter model'</span>,</span><br><span class="line">      theme: ThemeData(</span><br><span class="line">        primarySwatch: Colors.blue,</span><br><span class="line">        fontFamily: <span class="string">'PingFang'</span>,</span><br><span class="line">      ),</span><br><span class="line">      home: _widgetRoute(<span class="built_in">window</span>.defaultRouteName), <span class="comment">// 通过 window.defaultRouteName 接收 android 端传来的参数</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">Widget _widgetRoute(<span class="built_in">String</span> defaultRouteName) &#123;</span><br><span class="line">    <span class="built_in">Map</span>&lt;<span class="built_in">String</span>, <span class="keyword">dynamic</span>&gt; params = convert.jsonDecode(defaultRouteName); <span class="comment">//解析参数</span></span><br><span class="line">    defaultRouteName = params[<span class="string">'routeName'</span>];</span><br><span class="line">    placeHolder = params[<span class="string">'placeHolder'</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">switch</span> (defaultRouteName) &#123; <span class="comment">// 根据参数返回对应的页面</span></span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">case</span> <span class="string">'destination/search'</span>:</span><br><span class="line">            <span class="keyword">return</span> DestinationSearchPage(</span><br><span class="line">                hideLeft: <span class="keyword">false</span>,</span><br><span class="line">        );</span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">return</span> Center(</span><br><span class="line">                child: Column(</span><br><span class="line">                mainAxisAlignment: MainAxisAlignment.center,</span><br><span class="line">                children: &lt;Widget&gt;[</span><br><span class="line">                    Text(<span class="string">'not found <span class="subst">$defaultRouteName<span class="string">',</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">                        textDirection: TextDirection.ltr),</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">                ],</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">                ),</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">            );</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    &#125;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">&#125;</span></span></span></span><br></pre></td></tr></table></figure><p>其实，flutter 端接收这个 route 参数，还有一种方法，就是通过 <code>onGenerateRoute</code>，它是 MaterialApp 里的一个方法。</p><p>代码如下：</p><figure class="highlight dart"><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">onGenerateRoute: (settings)&#123; <span class="comment">//通过 settings.name 获取android端传来的参数</span></span><br><span class="line">    <span class="keyword">return</span> _widgetRoute(settings.name);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><h3 id="flutter-和-android-之间相互通信"><a href="#flutter-和-android-之间相互通信" class="headerlink" title="flutter 和 android 之间相互通信"></a>flutter 和 android 之间相互通信</h3><p>flutter 端可以调用 android 端的方法及相互传递数据是如何实现的，flutter 官方提供了3个方法可以实现，分别是：</p><ul><li>EventChannel：单向的持续通信，如：网络变化、传感器等。</li><li>MethodChannel：一次性通信，一般适用如方法的调用。</li><li>BasicMessageChannel：持续的双向通信。</li></ul><p>此项目里采用了 <code>MethodChannel</code> 方法进行通信，如：flutter 端调用 android 端的AI智能语音方法以及 flutter 打开 android 端页面就是用 <code>MethodChannel</code> 实现的。</p><p>flutter 端调用 android 端的AI智能语音方法代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="class"><span class="keyword">class</span> <span class="title">AsrManager</span> </span>&#123;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">const</span> MethodChannel _channel = <span class="keyword">const</span> MethodChannel(<span class="string">'lib_asr'</span>);</span><br><span class="line">  <span class="comment">//开始录音</span></span><br><span class="line">  <span class="keyword">static</span> Future&lt;<span class="built_in">String</span>&gt; start(&#123;<span class="built_in">Map</span> params&#125;) <span class="keyword">async</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> _channel.invokeMethod(<span class="string">'start'</span>, params ?? &#123;&#125;);</span><br><span class="line">  &#125;</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><br><span class="line">  <span class="comment">//销毁</span></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>flutter 打开 android 端页面代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="class"><span class="keyword">class</span> <span class="title">MethodChannelPlugin</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">const</span> MethodChannel methodChannel = MethodChannel(<span class="string">'MethodChannelPlugin'</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> Future&lt;<span class="keyword">void</span>&gt; gotoDestinationSearchPage() <span class="keyword">async</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">await</span> methodChannel.invokeMethod(<span class="string">'gotoDestinationSearchPage'</span>); <span class="comment">//gotoDestinationSearchPage 参数会传到android端</span></span><br><span class="line">    &#125; on PlatformException &#123;</span><br><span class="line">      <span class="built_in">print</span>(<span class="string">'Failed go to gotoDestinationSearchPage'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>android 接收也是通过 <code>MethodChannel</code> ，具体实现代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MethodChannelPlugin</span> <span class="keyword">implements</span> <span class="title">MethodChannel</span>.<span class="title">MethodCallHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> MethodChannel methodChannel;</span><br><span class="line">    <span class="keyword">private</span> Activity activity;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">MethodChannelPlugin</span><span class="params">(Activity activity)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.activity = activity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//调用方通过 registerWith 来注册flutter页面</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">registerWith</span><span class="params">(FlutterView flutterView)</span> </span>&#123;</span><br><span class="line">        methodChannel = <span class="keyword">new</span> MethodChannel(flutterView, <span class="string">"MethodChannelPlugin"</span>);</span><br><span class="line">        MethodChannelPlugin instance = <span class="keyword">new</span> MethodChannelPlugin((Activity) flutterView.getContext());</span><br><span class="line">        methodChannel.setMethodCallHandler(instance);</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">onMethodCall</span><span class="params">(MethodCall methodCall, MethodChannel.Result result)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (methodCall.method.equals(<span class="string">"gotoDestinationSearchPage"</span>)) &#123; <span class="comment">// 收到消息进行具体操作</span></span><br><span class="line">            EventBus.getDefault().post(<span class="keyword">new</span> GotoDestinationSearchPageEvent());</span><br><span class="line">            result.success(<span class="number">200</span>);</span><br><span class="line">        &#125; </span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            result.notImplemented();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>android flutter 混合开发基本就是这3个步骤，其他一些细节及具体的流程请参考 <a href="https://github.com/persilee/android_ctrip" target="_blank" rel="noopener">GitHub</a> 项目源码。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/androidCtrip.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Flutter 10天高仿大厂App及小技巧积累总结</title>
      <link>https://h.lishaoy.net/flutterCtrip.html</link>
      <guid>https://h.lishaoy.net/flutterCtrip.html</guid>
      <pubDate>Mon, 11 May 2020 13:53:05 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/flutterCtrip/iOS-andorid.png&quot; class=&quot;full-image&quot; alt=&quot;Flutter&quot; title=&quot;Flutter&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;之前，也写过几篇关于 &lt;code&gt;Flutter&lt;/code&gt; 的博文，最近，又花了一些时间学习研究 &lt;code&gt;Flutter&lt;/code&gt;，完成了高仿大厂 &lt;strong&gt;App&lt;/strong&gt; 项目 &lt;em&gt;(项目使用的接口都是来自线上真实App抓包而来，可以做到和线上项目相同的效果)&lt;/em&gt;，也总结积累了一些小技巧和知识点，所以，在这里记录分享出来，也希望 &lt;code&gt;Flutter&lt;/code&gt; 生态越来越好 &lt;em&gt;（flutter开发App效率真的很高，开发体验也是很好的 🙂）&lt;/em&gt;。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/flutterCtrip/iOS-andorid.png" class="full-image" alt="Flutter" title="Flutter"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>之前，也写过几篇关于 <code>Flutter</code> 的博文，最近，又花了一些时间学习研究 <code>Flutter</code>，完成了高仿大厂 <strong>App</strong> 项目 <em>(项目使用的接口都是来自线上真实App抓包而来，可以做到和线上项目相同的效果)</em>，也总结积累了一些小技巧和知识点，所以，在这里记录分享出来，也希望 <code>Flutter</code> 生态越来越好 <em>（flutter开发App效率真的很高，开发体验也是很好的 🙂）</em>。</p><hr><a id="more"></a><p>以下博文会分为4个部分概述：</p><ul><li>项目完成的功能预览</li><li>项目结构分析</li><li>项目功能详细概述（所用知识点）</li><li>小技巧积累总结</li></ul><h2 id="项目完成的功能预览"><a href="#项目完成的功能预览" class="headerlink" title="项目完成的功能预览"></a>项目完成的功能预览</h2><p>首先，我们来通过一个视频来快速预览下项目完成的功能和运行效果，如下</p><video id="flutter" class="video-js vjs-default-skin" controls preload="auto" poster="https://cdn.lishaoy.net/ctrip/ctripV.png" data-setup="{'example_option':true}">    <source src="https://cdn.lishaoy.net/flutterCtrip/ctrip.mp4" type="video/mp4"></video><div class="note warning"><p><i class="fa fa-fw fa-bell  faa-horizontal animated faa-slow" style="color: #faab33;"></i> 如视频播放失败， <a href="https://www.bilibili.com/video/BV16p4y1Q71f/" target="_blank" rel="noopener">请移步这里点击观看</a> （可点击齿轮设置隐藏黑边）</p></div><p><br>大家看完视频，大概了解到，完成度基本可以和线上的 <strong>App</strong> 相差无异了，大家如果对项目感兴趣，想了解具体怎么实现的，可以去我的 <a href="https://github.com/persilee" target="_blank" rel="noopener">GitHub</a> clone 源码查看。</p><p>也可扫描，安装体验：</p><div style="width:166px; margin:auto"><img src="https://www.pgyer.com/app/qrcode/HqqH" alt="no-shadow" title="手机扫描二维码安装"></div><p>本视频是用真机录屏的，因为，语音搜索功能需要录音，模拟器无法录音，当然, <code>iOS</code> 和 <code>Andorid</code>都可以运行，效果是一样的，如图：</p><p><img src="https://cdn.lishaoy.net/flutterCtrip/iOS-andorid.png" alt="no-shadow" title="iOS Andorid"></p><h2 id="项目结构分析"><a href="#项目结构分析" class="headerlink" title="项目结构分析"></a>项目结构分析</h2><p>其次，梳理下项目的目录结构，理解每个文件都是干什么的，我们先来看看一级目录，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">├── README.md  <span class="comment"># 描述文件</span></span><br><span class="line">├── android    <span class="comment"># android 宿主环境</span></span><br><span class="line">├── build      <span class="comment"># 项目构建目录，由flutter自动完成</span></span><br><span class="line">├── flutter_ctrip.iml</span><br><span class="line">├── fonts      <span class="comment"># 自己创建的目录，用于存放字体</span></span><br><span class="line">├── images     <span class="comment"># 自己创建的目录，用于存放图片</span></span><br><span class="line">├── ios        <span class="comment"># iOS 宿主环境</span></span><br><span class="line">├── lib        <span class="comment"># flutter 执行文件，自己写的代码都在这</span></span><br><span class="line">├── pubspec.lock <span class="comment"># 用来记录锁定插件版本</span></span><br><span class="line">├── pubspec.yaml <span class="comment"># 插件及资源配置文件</span></span><br><span class="line">└── <span class="built_in">test</span>       <span class="comment"># 测试目录</span></span><br></pre></td></tr></table></figure><p>这个就不用多解释，大多是 flutter 生成及管理的，我们需要关注的是 <strong>lib</strong> 目录。</p><p>我们再来看看二级目录，如下 <em>(重点关注下lib目录)</em></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">├── README.md</span><br><span class="line">├── android</span><br><span class="line">│   ├── android.iml</span><br><span class="line">  ...</span><br><span class="line">│   └── settings.gradle</span><br><span class="line">├── build</span><br><span class="line">│   ├── app</span><br><span class="line">  ...</span><br><span class="line">│   └── snapshot_blob.bin.d.fingerprint</span><br><span class="line">├── flutter_ctrip.iml</span><br><span class="line">├── fonts</span><br><span class="line">│   ├── PingFang-Italic.ttf</span><br><span class="line">│   ├── PingFang-Regular.ttf</span><br><span class="line">│   └── PingFang_Bold.ttf</span><br><span class="line">├── images</span><br><span class="line">│   ├── grid-nav-items-dingzhi.png</span><br><span class="line">  ...</span><br><span class="line">│   └── yuyin.png</span><br><span class="line">├── ios</span><br><span class="line">│   ├── Flutter</span><br><span class="line">  ...</span><br><span class="line">│   └── ServiceDefinitions.json</span><br><span class="line">├── lib</span><br><span class="line">│   ├── dao           <span class="comment"># 请求接口的类</span></span><br><span class="line">│   ├── main.dart     <span class="comment"># flutter 入口文件</span></span><br><span class="line">│   ├── model         <span class="comment"># 实体类，把服务器返回的 json 数据，转换成 dart 类</span></span><br><span class="line">│   ├── navigator     <span class="comment"># bottom bar 首页底部导航路由</span></span><br><span class="line">│   ├── pages         <span class="comment"># 所以的页面</span></span><br><span class="line">│   ├── plugin        <span class="comment"># 封装的插件</span></span><br><span class="line">│   ├── util          <span class="comment"># 工具类，避免重复代码，封装成工具类以便各个 page 调用</span></span><br><span class="line">│   └── widget        <span class="comment"># 封装的组件</span></span><br><span class="line">├── pubspec.lock</span><br><span class="line">├── pubspec.yaml</span><br><span class="line">└── <span class="built_in">test</span></span><br><span class="line">    └── widget_test.dart</span><br></pre></td></tr></table></figure><p>再来看看，<strong>lib</strong> 目录下二级目录，看看整个项目创建了多少个文件，写了多少代码，如下 <em>（其实，并不是很多）</em></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">├── dao/</span><br><span class="line">│   ├── destination_dao.dart*</span><br><span class="line">│   ├── destination_search_dao.dart*</span><br><span class="line">│   ├── home_dao.dart</span><br><span class="line">│   ├── search_dao.dart*</span><br><span class="line">│   ├── trave_hot_keyword_dao.dart*</span><br><span class="line">│   ├── trave_search_dao.dart*</span><br><span class="line">│   ├── trave_search_hot_dao.dart*</span><br><span class="line">│   ├── travel_dao.dart*</span><br><span class="line">│   ├── travel_params_dao.dart*</span><br><span class="line">│   └── travel_tab_dao.dart*</span><br><span class="line">├── main.dart</span><br><span class="line">├── model/</span><br><span class="line">│   ├── common_model.dart</span><br><span class="line">│   ├── config_model.dart</span><br><span class="line">│   ├── destination_model.dart</span><br><span class="line">│   ├── destination_search_model.dart</span><br><span class="line">│   ├── grid_nav_model.dart</span><br><span class="line">│   ├── home_model.dart</span><br><span class="line">│   ├── sales_box_model.dart</span><br><span class="line">│   ├── seach_model.dart*</span><br><span class="line">│   ├── travel_hot_keyword_model.dart</span><br><span class="line">│   ├── travel_model.dart*</span><br><span class="line">│   ├── travel_params_model.dart*</span><br><span class="line">│   ├── travel_search_hot_model.dart</span><br><span class="line">│   ├── travel_search_model.dart</span><br><span class="line">│   └── travel_tab_model.dart</span><br><span class="line">├── navigator/</span><br><span class="line">│   └── tab_navigater.dart</span><br><span class="line">├── pages/</span><br><span class="line">│   ├── destination_page.dart</span><br><span class="line">│   ├── destination_search_page.dart</span><br><span class="line">│   ├── home_page.dart</span><br><span class="line">│   ├── my_page.dart</span><br><span class="line">│   ├── search_page.dart</span><br><span class="line">│   ├── speak_page.dart*</span><br><span class="line">│   ├── test_page.dart</span><br><span class="line">│   ├── travel_page.dart</span><br><span class="line">│   ├── travel_search_page.dart</span><br><span class="line">│   └── travel_tab_page.dart*</span><br><span class="line">├── plugin/</span><br><span class="line">│   ├── asr_manager.dart*</span><br><span class="line">│   ├── side_page_view.dart</span><br><span class="line">│   ├── square_swiper_pagination.dart</span><br><span class="line">│   └── vertical_tab_view.dart</span><br><span class="line">├── util/</span><br><span class="line">│   └── navigator_util.dart*</span><br><span class="line">└── widget/</span><br><span class="line">    ├── grid_nav.dart</span><br><span class="line">    ├── grid_nav_new.dart</span><br><span class="line">    ├── loading_container.dart</span><br><span class="line">    ├── local_nav.dart</span><br><span class="line">    ├── sales_box.dart</span><br><span class="line">    ├── scalable_box.dart</span><br><span class="line">    ├── search_bar.dart*</span><br><span class="line">    ├── sub_nav.dart</span><br><span class="line">    └── webview.dart</span><br></pre></td></tr></table></figure><p>整个项目就是以上这些文件了 <em>（具体的就不一个一个分析了，如，感兴趣，大家可以 clone 源码运行起来，自然就清除了）</em>。</p><h2 id="项目功能详细概述（所用知识点）"><a href="#项目功能详细概述（所用知识点）" class="headerlink" title="项目功能详细概述（所用知识点）"></a>项目功能详细概述（所用知识点）</h2><p>首先，来看看首页功能及所用知识点，首页重点看下以下功能实现：</p><ul><li>渐隐渐现的 <strong>appBar</strong> </li><li>搜索组件的封装</li><li>语音搜索页面</li><li>banner组件</li><li>浮动的 icon 导航</li><li>渐变不规则带有背景图的网格导航</li></ul><h3 id="渐隐渐现的-appBar"><a href="#渐隐渐现的-appBar" class="headerlink" title="渐隐渐现的 appBar"></a>渐隐渐现的 appBar</h3><p>先来看看具体效果，一睹芳容，如图：</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/appBar.gif" alt="no-shadow" title="appBar"></div><p>滚动的时候 <strong>appBar</strong> 背景色从透明变成白色或白色变成透明，这里主要用了 <strong>flutter</strong> 的 <code>NotificationListener</code> 组件，它会去监听组件树冒泡事件，当被它包裹的的组件<em>（子组件）</em> 发生变化时，<code>Notification</code> 回调函数会被触发，所以，通过它可以去监听页面的滚动，来动态改变 <strong>appBar</strong> 的透明度<em>（alpha）</em>，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">NotificationListener(</span><br><span class="line">  onNotification: (scrollNotification) &#123;</span><br><span class="line">    <span class="keyword">if</span> (scrollNotification <span class="keyword">is</span> ScrollUpdateNotification &amp;&amp;</span><br><span class="line">        scrollNotification.depth == <span class="number">0</span>) &#123;</span><br><span class="line">      _onScroll(scrollNotification.metrics.pixels);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">  &#125;,</span><br><span class="line">  child: ...</span><br></pre></td></tr></table></figure><div class="note warning"><p><i class="fa fa-fw fa-bell  faa-horizontal animated faa-slow" style="color: #faab33;"></i> <strong>Tips：</strong> <span class="label danger">scrollNotification.depth</span>的值 0 表示其子组件<em>(只监听子组件，不监听孙组件)</em>；<span class="label danger">scrollNotification is ScrollUpdateNotification</span>来判断组件是否已更新，<strong>ScrollUpdateNotification</strong> 是 notifications 的生命周期一种情况，分别有一下几种：</p><ul><li>ScrollStartNotification 组件开始滚动</li><li>ScrollUpdateNotification 组件位置已经发生改变</li><li>ScrollEndNotification 组件停止滚动</li><li>UserScrollNotification 不清楚</li></ul><p>这里，我们不探究太深入，如想了解可多查看源码。</p></div> <p><strong>_onScroll</strong> 方法代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> _onScroll(offset) &#123;</span><br><span class="line">  <span class="built_in">double</span> alpha = offset / APPBAR_SCROLL_OFFSET;  <span class="comment">// APPBAR_SCROLL_OFFSET 常量，值：100；offset 滚动的距离</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">//把 alpha 值控制值 0-1 之间</span></span><br><span class="line">  <span class="keyword">if</span> (alpha &lt; <span class="number">0</span>) &#123;</span><br><span class="line">    alpha = <span class="number">0</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> <span class="keyword">if</span> (alpha &gt; <span class="number">1</span>) &#123;</span><br><span class="line">    alpha = <span class="number">1</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  setState(() &#123;</span><br><span class="line">    appBarAlpha = alpha;</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="built_in">print</span>(alpha);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="搜索组件的封装"><a href="#搜索组件的封装" class="headerlink" title="搜索组件的封装"></a>搜索组件的封装</h3><p>搜索组件效果如图：</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/searchBar.gif" alt="no-shadow" title="searchBar"></div><p>以下是首页调用 <code>searchBar</code> 的代码：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">SearchBar(</span><br><span class="line">  searchBarType: appBarAlpha &gt; <span class="number">0.2</span>  <span class="comment">//searchBar 的类：暗色、亮色</span></span><br><span class="line">      ? SearchBarType.homeLight</span><br><span class="line">      : SearchBarType.home,</span><br><span class="line">  inputBoxClick: _jumpToSearch,     <span class="comment">//点击回调函数</span></span><br><span class="line">  defaultText: SEARCH_BAR_DEFAULT_TEXT,   <span class="comment">// 提示文字</span></span><br><span class="line">  leftButtonClick: () &#123;&#125;,           <span class="comment">//左边边按钮点击回调函数</span></span><br><span class="line">  speakClick: _jumpToSpeak,         <span class="comment">//点击话筒回调函数</span></span><br><span class="line">  rightButtonClick: _jumpToUser,    <span class="comment">//右边边按钮点击回调函数</span></span><br><span class="line">),</span><br></pre></td></tr></table></figure><p>其实就是用 <code>TextField</code> 组件，再加一些样式，需要注意点是：<strong>onChanged</strong>，他是 <strong>TextField</strong> 用来监听文本框是否变化，通过它我们来监听用户输入，来请求接口数据;具体的实现细节，请查阅源码： <a href="https://github.com/persilee/flutter_ctrip/blob/master/lib/widget/search_bar.dart" target="_blank" rel="noopener">点击查看searchBar源码</a></p><h3 id="语音搜索页面"><a href="#语音搜索页面" class="headerlink" title="语音搜索页面"></a>语音搜索页面</h3><p>语音搜索页面效果如图：由于模拟器无法录音，所以无法展示正常流程，如果录音识别成功后会返回搜索页面，在项目预览视频中可以看到正常流程。</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/speak.gif" alt="no-shadow" title="speak"></div><p>语音搜索功能使用的是百度的语言识别SDK，原生接入之后，通过 <strong>MethodChannel</strong> 和原生Native端通信，这里不做重点讲述（这里会涉及原生Native的知识）。</p><p>重点看看点击录音按钮时的动画实现，这个动画用了 <strong>AnimatedWidget</strong> 实现的，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="class"><span class="keyword">class</span> <span class="title">AnimatedWear</span> <span class="keyword">extends</span> <span class="title">AnimatedWidget</span> </span>&#123;</span><br><span class="line">  <span class="keyword">final</span> <span class="built_in">bool</span> isStart;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">final</span> _opacityTween = Tween&lt;<span class="built_in">double</span>&gt;(begin: <span class="number">0.5</span>, end: <span class="number">0</span>); <span class="comment">// 设置透明度变化值</span></span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">final</span> _sizeTween = Tween&lt;<span class="built_in">double</span>&gt;(begin: <span class="number">90</span>, end: <span class="number">260</span>);   <span class="comment">// 设置圆形线的扩散值</span></span><br><span class="line"></span><br><span class="line">  AnimatedWear(&#123;Key key, <span class="keyword">this</span>.isStart, Animation&lt;<span class="built_in">double</span>&gt; animation&#125;)</span><br><span class="line">      : <span class="keyword">super</span>(key: key, listenable: animation);</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">final</span> Animation&lt;<span class="built_in">double</span>&gt; animation = listenable;  <span class="comment">// listenable 继承 AnimatedWidget，其实就是控制器，会自动监听组件的变化</span></span><br><span class="line">    <span class="keyword">return</span> Container(</span><br><span class="line">      height: <span class="number">90</span>,</span><br><span class="line">      width: <span class="number">90</span>,</span><br><span class="line">      child: Stack(</span><br><span class="line">        overflow: Overflow.visible,</span><br><span class="line">        alignment: Alignment.center,</span><br><span class="line">        children: &lt;Widget&gt;[</span><br><span class="line">          ...</span><br><span class="line">          <span class="comment">// 扩散的圆线，其实就是用一个圆实现的，设置圆为透明，设置border</span></span><br><span class="line">          Positioned(</span><br><span class="line">            left: -((_sizeTween.evaluate(animation) - <span class="number">90</span>) / <span class="number">2</span>), <span class="comment">// 根据 _sizeTween 动态设置left偏移值</span></span><br><span class="line">            top: -((_sizeTween.evaluate(animation) - <span class="number">90</span>) / <span class="number">2</span>), <span class="comment">//  根据 _sizeTween 动态设置top偏移值</span></span><br><span class="line">            child: Opacity(</span><br><span class="line">              opacity: _opacityTween.evaluate(animation),      <span class="comment">// 根据 _opacityTween 动态设置透明值</span></span><br><span class="line">              child: Container(</span><br><span class="line">                width: isStart ? _sizeTween.evaluate(animation) : <span class="number">0</span>, <span class="comment">// 设置 宽</span></span><br><span class="line">                height: _sizeTween.evaluate(animation),              <span class="comment">// 设置 高</span></span><br><span class="line">                decoration: BoxDecoration(</span><br><span class="line">                    color: Colors.transparent,</span><br><span class="line">                    borderRadius: BorderRadius.circular(</span><br><span class="line">                        _sizeTween.evaluate(animation) / <span class="number">2</span>),</span><br><span class="line">                    border: Border.all(</span><br><span class="line">                      color: Color(<span class="number">0xa8000000</span>),</span><br><span class="line">                    )),</span><br><span class="line">              ),</span><br><span class="line">            ),</span><br><span class="line">          ),</span><br><span class="line">        ],</span><br><span class="line">      ),</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>其他细节，如：点击时提示录音，录音失败提示，点击录音按钮出现半透明黑色圆边框，停止后消失等，<a href="https://github.com/persilee/flutter_ctrip/blob/master/lib/pages/speak_page.dart" target="_blank" rel="noopener">请查看源码</a>。</p><h3 id="banner组件"><a href="#banner组件" class="headerlink" title="banner组件"></a>banner组件</h3><p>效果如图：</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/banner.gif" alt="no-shadow" title="banner"></div><p><code>banner</code>使用的是flutter的 <a href="https://pub.dev/packages/flutter_swiper" target="_blank" rel="noopener">flutter_swiper</a> 插件实现的，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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">Swiper(</span><br><span class="line">  itemCount: bannerList.length,              <span class="comment">// 滚动图片的数量</span></span><br><span class="line">  autoplay: <span class="keyword">true</span>,                            <span class="comment">// 自动播放</span></span><br><span class="line">  pagination: SwiperPagination(              <span class="comment">// 指示器</span></span><br><span class="line">      builder: SquareSwiperPagination(</span><br><span class="line">        size: <span class="number">6</span>,                             <span class="comment">// 指示器的大小</span></span><br><span class="line">        activeSize: <span class="number">6</span>,                       <span class="comment">// 激活状态指示器的大小</span></span><br><span class="line">        color: Colors.white.withAlpha(<span class="number">80</span>),   <span class="comment">// 颜色</span></span><br><span class="line">        activeColor: Colors.white,           <span class="comment">// 激活状态的颜色</span></span><br><span class="line">      ),</span><br><span class="line">    alignment: Alignment.bottomRight,        <span class="comment">// 对齐方式</span></span><br><span class="line">    margin: EdgeInsets.fromLTRB(<span class="number">0</span>, <span class="number">0</span>, <span class="number">14</span>, <span class="number">28</span>), <span class="comment">// 边距</span></span><br><span class="line">  ),</span><br><span class="line">  itemBuilder: (BuildContext context, <span class="built_in">int</span> index) &#123; <span class="comment">// 构造器</span></span><br><span class="line">    <span class="keyword">return</span> GestureDetector(</span><br><span class="line">      onTap: () &#123;</span><br><span class="line">        CommonModel model = bannerList[index];</span><br><span class="line">        Navigator.push(</span><br><span class="line">          context,</span><br><span class="line">          MaterialPageRoute(</span><br><span class="line">            builder: (context) =&gt; WebView(</span><br><span class="line">              url: model.url,</span><br><span class="line">            ),</span><br><span class="line">          ),</span><br><span class="line">        );</span><br><span class="line">      &#125;,</span><br><span class="line">      child: Image.network(</span><br><span class="line">        bannerList[index].icon,</span><br><span class="line">        fit: BoxFit.fill,</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;,</span><br><span class="line">),</span><br></pre></td></tr></table></figure><p>具体使用方法，可以去 flutter的官方插件库 <a href="https://pub.dev/" target="_blank" rel="noopener">pub.dev</a> 查看：<a href="https://pub.dev/packages/flutter_swiper" target="_blank" rel="noopener">点击flutter_swiper查看</a>。<div class="note warning"><p><i class="fa fa-fw fa-bell  faa-horizontal animated faa-slow" style="color: #faab33;"></i> <strong>Tips：</strong> 需要注意的是，我稍改造了一下指示器的样式，<code>flutter_swiper</code> 只提供了 3 种指示器样式，如下：</p><ul><li>dots = const DotSwiperPaginationBuilder()，圆形</li><li>fraction = const FractionPaginationBuilder()，百分数类型的,如：1/6，表示6页的第一页</li><li>rect = const RectSwiperPaginationBuilder()，矩形</li></ul><p>并没有上图的激活状态的长椭圆形，其实就是按葫芦画瓢，自己实现一个长椭圆类型，如知详情，可<a href="https://github.com/persilee/flutter_ctrip/blob/master/lib/plugin/square_swiper_pagination.dart" target="_blank" rel="noopener">点击查看长椭圆形指示器源码</a></p></div> </p><h3 id="浮动的-icon-导航"><a href="#浮动的-icon-导航" class="headerlink" title="浮动的 icon 导航"></a>浮动的 icon 导航</h3><p><strong>icon导航</strong>效果如图：</p><p><img src="https://cdn.lishaoy.net/flutterCtrip/iconBar.png" alt="iconBar" width="36%" title="iconBar" align="center"></p><p><strong>icon导航</strong>浮动在banner之上，其实用的是 <code>flutter</code> 的 <strong>Stack</strong> 组件，Stack 组件能让其子组件堆叠显示，它通常和 <strong>Positioned</strong> 组件配合使用，布局结构代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">ListView(</span><br><span class="line">  children: &lt;Widget&gt;[</span><br><span class="line">    Container(</span><br><span class="line">      child: Stack(</span><br><span class="line">        children: &lt;Widget&gt;[</span><br><span class="line">          Container( ... ), <span class="comment">//这里放的是banner的代码</span></span><br><span class="line">          Positioned( ... ), <span class="comment">//这个就是icon导航，通过 Positioned 固定显示位置</span></span><br><span class="line">        ],</span><br><span class="line">      ),</span><br><span class="line">    ),</span><br><span class="line">    Container( ... ), <span class="comment">// 这里放的网格导航及其他</span></span><br><span class="line">  ],</span><br><span class="line">),</span><br></pre></td></tr></table></figure><h3 id="渐变不规则带有背景图的网格导航"><a href="#渐变不规则带有背景图的网格导航" class="headerlink" title="渐变不规则带有背景图的网格导航"></a>渐变不规则带有背景图的网格导航</h3><p>网格导航效果如图：</p><p><img src="https://cdn.lishaoy.net/flutterCtrip/gridNav.png" alt="gridNav" width="46%" title="gridNav" align="center"></p><p>如图，网格导航分为三行四栏，而第一行分为三栏，每一行的第一栏宽度大于其余三栏，其余三栏均等，每一行都有渐变色，而且第一、二栏都有背景图;<code>flutter</code> 里 <strong>Column</strong> 组件能让子组件竖轴排列， <strong>Row</strong> 组件能让子组件横轴排列，布局代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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">Column(                      <span class="comment">// 最外面放在 Column 组件</span></span><br><span class="line">  children: &lt;Widget&gt;[</span><br><span class="line">    Container(               <span class="comment">// 第一行包裹 Container 设置其渐变色</span></span><br><span class="line">      height: <span class="number">72</span>,</span><br><span class="line">      decoration: BoxDecoration(</span><br><span class="line">        gradient: LinearGradient(colors: [  <span class="comment">//设置渐变色</span></span><br><span class="line">          Color(<span class="number">0xfffa5956</span>),</span><br><span class="line">          Color(<span class="number">0xffef9c76</span>).withAlpha(<span class="number">45</span>)</span><br><span class="line">        ]),</span><br><span class="line">      ),</span><br><span class="line">      child: Row( ... ),    <span class="comment">// 第一行</span></span><br><span class="line">    ),</span><br><span class="line">    Padding(</span><br><span class="line">      padding: EdgeInsets.only(top: <span class="number">1</span>),  <span class="comment">// 设置行直接的间隔</span></span><br><span class="line">    ),</span><br><span class="line">    Container(</span><br><span class="line">      height: <span class="number">72</span>,</span><br><span class="line">      decoration: BoxDecoration(</span><br><span class="line">        gradient: LinearGradient(colors: [  <span class="comment">//设置渐变色</span></span><br><span class="line">          Color(<span class="number">0xff4b8fed</span>),</span><br><span class="line">          Color(<span class="number">0xff53bced</span>),</span><br><span class="line">        ]),</span><br><span class="line">      ),</span><br><span class="line">      child: Row( ... ),  <span class="comment">// 第二行</span></span><br><span class="line">    ),</span><br><span class="line">    Padding(</span><br><span class="line">      padding: EdgeInsets.only(top: <span class="number">1</span>),   <span class="comment">// 设置行直接的间隔</span></span><br><span class="line">    ),</span><br><span class="line">    Container(</span><br><span class="line">      height: <span class="number">72</span>,</span><br><span class="line">      decoration: BoxDecoration(</span><br><span class="line">        gradient: LinearGradient(colors: [  <span class="comment">//设置渐变色</span></span><br><span class="line">          Color(<span class="number">0xff34c2aa</span>),</span><br><span class="line">          Color(<span class="number">0xff6cd557</span>),</span><br><span class="line">        ]),</span><br><span class="line">      ),</span><br><span class="line">      child: Row( ... ),  <span class="comment">// 第三行</span></span><br><span class="line">    ),</span><br><span class="line">  ],</span><br><span class="line">),</span><br></pre></td></tr></table></figure><p>其实，具体实现的细节还是很多的，比如：</p><ul><li>怎么设置第一栏宽度偏大，其他均等；</li><li>第一行最后一栏宽度是其他的2倍；</li><li>第一、二栏的背景图及浮动的红色气泡tip等;</li></ul><p>在这里就不细讲，否则篇幅太长，如想了解详情 <a href="https://github.com/persilee/flutter_ctrip/blob/master/lib/widget/grid_nav_new.dart" target="_blank" rel="noopener">点击查看源码</a></p><p>其次，再来看看<strong>目的地</strong>页面功能及所用知识点，重点看下以下功能实现：</p><ul><li>左右布局tabBarListView </li><li>目的地搜索页面</li></ul><h3 id="左右布局tabBarListView"><a href="#左右布局tabBarListView" class="headerlink" title="左右布局tabBarListView"></a>左右布局tabBarListView</h3><p>具体效果如图：点击左边标签可以切换页面，左右滑动也可切换页面，点击展开显示更多等</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/destination.gif" alt="no-shadow" title="destination"></div><p>其实官方已经提供了 <strong>tabBar</strong> 和 <strong>TabBarView</strong> 组件可以实现上下布局的效果<em>(旅拍页面就是用这个实现的)</em>，但是它无法实现左右布局，而且不太灵活，所以，我使用的是 <a href="https://pub.dev/packages/vertical_tabs" target="_blank" rel="noopener">vertical_tabs</a>插件, 代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">VerticalTabView(</span><br><span class="line">    tabsWidth: <span class="number">88</span>,</span><br><span class="line">    tabsElevation: <span class="number">0</span>,</span><br><span class="line">    indicatorWidth: <span class="number">0</span>,</span><br><span class="line">    selectedTabBackgroundColor: Colors.white,</span><br><span class="line">    backgroundColor: Colors.white,</span><br><span class="line">    tabTextStyle: TextStyle(</span><br><span class="line">      height: <span class="number">60</span>,</span><br><span class="line">      color: Color(<span class="number">0xff333333</span>),</span><br><span class="line">    ),</span><br><span class="line">    tabs: tabs,</span><br><span class="line">    contents: tabPages,</span><br><span class="line">  ),</span><br><span class="line">),</span><br></pre></td></tr></table></figure><p>具体使用方法，在这里就不赘述了，<a href="https://pub.dev/packages/vertical_tabs" target="_blank" rel="noopener">点击vertical_tabs查看</a></p><div class="note warning"><p><i class="fa fa-fw fa-bell  faa-horizontal animated faa-slow" style="color: #faab33;"></i> <strong>Tips：</strong> 这里需要注意的是：<strong>展开</strong>显示更多span标签组件的实现，因为，这个组件在很多的其他组件里用到而且要根据接口数据动态渲染，且组件自身存在状态的变化，这种情况下，最好是把他单独封装成一个组件<em>(widget)</em>，否则，很难控制自身状态的变化，出现点击没有效果，或点击影响其他组件。 </p></div> <h3 id="目的地搜索页面"><a href="#目的地搜索页面" class="headerlink" title="目的地搜索页面"></a>目的地搜索页面</h3><p>效果如图：点击搜索结果，如：点击‘一日游‘，会搜索到‘一日游‘的相关数据</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/destination-search.gif" alt="no-shadow" title="destination"></div><p>目的地搜索页面，大多都是和布局和对接接口的代码，在这里就不再赘述。</p><p>然后就是<strong>旅拍页面</strong>功能及所用知识点，重点看下以下功能实现：</p><ul><li>左右布局tabBarListView</li><li>瀑布流卡片</li><li>旅拍搜索页</li></ul><h3 id="左右布局tabBarListView-1"><a href="#左右布局tabBarListView-1" class="headerlink" title="左右布局tabBarListView"></a>左右布局tabBarListView</h3><p>效果如图：可左右滑动切换页面，上拉加载更多，下拉刷新等</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/travel.gif" alt="no-shadow" title="travel"></div><p>这个是<code>flutter</code> 提供的组件，<strong>tabBar</strong> 和 <strong>TabBarView</strong>，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">Container(</span><br><span class="line">  color: Colors.white,</span><br><span class="line">  padding: EdgeInsets.only(left: <span class="number">2</span>),</span><br><span class="line">  child: TabBar(</span><br><span class="line">    controller: _controller,</span><br><span class="line">    isScrollable: <span class="keyword">true</span>,</span><br><span class="line">    labelColor: Colors.black,</span><br><span class="line">    labelPadding: EdgeInsets.fromLTRB(<span class="number">8</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">0</span>),</span><br><span class="line">    indicatorColor: Color(<span class="number">0xff2FCFBB</span>),</span><br><span class="line">    indicatorPadding: EdgeInsets.all(<span class="number">6</span>),</span><br><span class="line">    indicatorSize: TabBarIndicatorSize.label,</span><br><span class="line">    indicatorWeight: <span class="number">2.2</span>,</span><br><span class="line">    labelStyle: TextStyle(fontSize: <span class="number">18</span>),</span><br><span class="line">    unselectedLabelStyle: TextStyle(fontSize: <span class="number">15</span>),</span><br><span class="line">    tabs: tabs.map&lt;Tab&gt;((Groups tab) &#123;</span><br><span class="line">      <span class="keyword">return</span> Tab(</span><br><span class="line">        text: tab.name,</span><br><span class="line">      );</span><br><span class="line">    &#125;).toList(),</span><br><span class="line">  ),</span><br><span class="line">),</span><br><span class="line">Flexible(</span><br><span class="line">    child: Container(</span><br><span class="line">  padding: EdgeInsets.fromLTRB(<span class="number">6</span>, <span class="number">3</span>, <span class="number">6</span>, <span class="number">0</span>),</span><br><span class="line">  child: TabBarView(</span><br><span class="line">      controller: _controller,</span><br><span class="line">      children: tabs.map((Groups tab) &#123;</span><br><span class="line">        <span class="keyword">return</span> TravelTabPage(</span><br><span class="line">          travelUrl: travelParamsModel?.url,</span><br><span class="line">          params: travelParamsModel?.params,</span><br><span class="line">          groupChannelCode: tab?.code,</span><br><span class="line">        );</span><br><span class="line">      &#125;).toList()),</span><br><span class="line">)),</span><br></pre></td></tr></table></figure><h3 id="瀑布流卡片"><a href="#瀑布流卡片" class="headerlink" title="瀑布流卡片"></a>瀑布流卡片</h3><p><strong>瀑布流卡片</strong> 用的是 <a href="https://pub.dev/packages/flutter_staggered_grid_view" target="_blank" rel="noopener">flutter_staggered_grid_view</a> 插件，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">StaggeredGridView.countBuilder(</span><br><span class="line">  controller: _scrollController,</span><br><span class="line">  crossAxisCount: <span class="number">4</span>,</span><br><span class="line">  itemCount: travelItems?.length ?? <span class="number">0</span>,</span><br><span class="line">  itemBuilder: (BuildContext context, <span class="built_in">int</span> index) =&gt; _TravelItem(</span><br><span class="line">        index: index,</span><br><span class="line">        item: travelItems[index],</span><br><span class="line">      ),</span><br><span class="line">  staggeredTileBuilder: (<span class="built_in">int</span> index) =&gt; <span class="keyword">new</span> StaggeredTile.fit(<span class="number">2</span>),</span><br><span class="line">  mainAxisSpacing: <span class="number">2.0</span>,</span><br><span class="line">  crossAxisSpacing: <span class="number">2.0</span>,</span><br><span class="line">),</span><br></pre></td></tr></table></figure><p>如下了解更多相关信息，<a href="https://pub.dev/packages/flutter_staggered_grid_view" target="_blank" rel="noopener">点击flutter_staggered_grid_view查看</a>。</p><h3 id="旅拍搜索页"><a href="#旅拍搜索页" class="headerlink" title="旅拍搜索页"></a>旅拍搜索页</h3><p>效果如图：首先显示热门旅拍标签，点击可搜索相关内容，输入关键字可搜索相关旅拍信息，地点、景点、用户等</p><div style="width:36%; margin:auto"><img src="https://cdn.lishaoy.net/flutterCtrip/travel-search.gif" alt="no-shadow" title="travel-search"></div><p>旅拍搜索页，大多也是和布局和对接接口的代码，在这里就不再赘述。</p><h2 id="小技巧积累总结"><a href="#小技巧积累总结" class="headerlink" title="小技巧积累总结"></a>小技巧积累总结</h2><p>以下都是我在项目里使用的知识点，在这里记录分享出来，希望能帮到大家。</p><h3 id="PhysicalModel"><a href="#PhysicalModel" class="headerlink" title="PhysicalModel"></a>PhysicalModel</h3><p><strong>PhysicalModel</strong> 可以裁剪带背景图的容器，如，你在一个 Container 里放了一张图片，想设置图片圆角，设置 Container 的 decoration 的 borderRadius 是无效的，这时候就要用到 <strong>PhysicalModel</strong>，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">PhysicalModel(</span><br><span class="line">  borderRadius: BorderRadius.circular(<span class="number">6</span>),  <span class="comment">// 设置圆角</span></span><br><span class="line">  clipBehavior: Clip.antiAlias,            <span class="comment">// 裁剪行为</span></span><br><span class="line">  color: Colors.transparent,               <span class="comment">// 颜色</span></span><br><span class="line">  elevation: <span class="number">5</span>,                            <span class="comment">// 设置阴影</span></span><br><span class="line">  child: Container(</span><br><span class="line">        child: Image.network(</span><br><span class="line">          picUrl,</span><br><span class="line">          fit: BoxFit.cover,</span><br><span class="line">        ),</span><br><span class="line">      ),</span><br><span class="line">),</span><br></pre></td></tr></table></figure><h3 id="LinearGradient"><a href="#LinearGradient" class="headerlink" title="LinearGradient"></a>LinearGradient</h3><p>给容器添加渐变色，在网格导航、appBar等地方都使用到，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">Container(</span><br><span class="line">  height: <span class="number">72</span>,</span><br><span class="line">  decoration: BoxDecoration(</span><br><span class="line">    gradient: LinearGradient(colors: [</span><br><span class="line">      Color(<span class="number">0xff4b8fed</span>),</span><br><span class="line">      Color(<span class="number">0xff53bced</span>),</span><br><span class="line">    ]),</span><br><span class="line">  ),</span><br><span class="line">  child: ...</span><br><span class="line">),</span><br></pre></td></tr></table></figure><h3 id="Color-int-parse-‘0xff’-gridNavItem-startColor"><a href="#Color-int-parse-‘0xff’-gridNavItem-startColor" class="headerlink" title="Color(int.parse(‘0xff’ + gridNavItem.startColor))"></a>Color(int.parse(‘0xff’ + gridNavItem.startColor))</h3><p>颜色值转换成颜色，如果，没有变量的话，也可直接这样用 <code>Color(0xff53bced)</code>，</p><ul><li>ox：flutter要求，可固定不变</li><li>ff：代表透明度，不知道如何设置的话，可以用取色器，或者 withOpacity(opacity) 、 withAlpha(a)</li><li>53bced: 常规的6位RGB值</li></ul><h3 id="Expanded、FractionallySizedBox"><a href="#Expanded、FractionallySizedBox" class="headerlink" title="Expanded、FractionallySizedBox"></a>Expanded、FractionallySizedBox</h3><p><strong>Expanded</strong> 可以让子组件撑满父容器，通常和 <strong>Row</strong> 及 <strong>Column</strong> 组件搭配使用；<br><strong>FractionallySizedBox</strong> 可以让子组件撑满或超出父容器，可以单独使用，大小受 widthFactor 和 heightFactor 宽高因子的影响</p><h3 id="MediaQuery-removePadding"><a href="#MediaQuery-removePadding" class="headerlink" title="MediaQuery.removePadding"></a>MediaQuery.removePadding</h3><p><strong>MediaQuery.removePadding</strong> 可以移除组件的边距，有些组件自带有边距，有时候布局的时候，不需要边距，这时候就可以用 <strong>MediaQuery.removePadding</strong>，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">MediaQuery.removePadding(</span><br><span class="line">  removeTop: <span class="keyword">true</span>,</span><br><span class="line">  context: context,</span><br><span class="line">  child: ...</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="MediaQuery-of-context-size-width"><a href="#MediaQuery-of-context-size-width" class="headerlink" title="MediaQuery.of(context).size.width"></a>MediaQuery.of(context).size.width</h3><p><strong>MediaQuery.of(context).size.width</strong> 获取屏幕的宽度，同理，<strong>MediaQuery.of(context).size.height</strong> 获取屏幕的高度；如，想一行平均3等分： 0.3 * MediaQuery.of(context).size.width，在<strong>目的地页面</strong>的标签组件就使用到它，代码如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">Container(</span><br><span class="line">  alignment: Alignment.center,</span><br><span class="line">  ...</span><br><span class="line">  width: <span class="number">0.3</span>*MediaQuery.of(context).size.width - <span class="number">12</span>, <span class="comment">// 屏幕平分三等分， - 12 是给每份中间留出空间 </span></span><br><span class="line">  height: <span class="number">40</span>,</span><br><span class="line">  ...</span><br><span class="line">  child: ...</span><br><span class="line">),</span><br></pre></td></tr></table></figure><h3 id="Theme-of-context-platform-TargetPlatform-iOS"><a href="#Theme-of-context-platform-TargetPlatform-iOS" class="headerlink" title="Theme.of(context).platform == TargetPlatform.iOS"></a>Theme.of(context).platform == TargetPlatform.iOS</h3><p>判断操作系统类型，有时候可能有给 Andorid 和 iOS 做出不同的布局，就需要用到它。</p><h3 id="with-AutomaticKeepAliveClientMixin"><a href="#with-AutomaticKeepAliveClientMixin" class="headerlink" title="with AutomaticKeepAliveClientMixin"></a>with AutomaticKeepAliveClientMixin</h3><p><code>flutter</code> 在切换页面时候每次都会重新加载数据，如果想让页面保留状态，不重新加载，就需要使用 <strong>AutomaticKeepAliveClientMixin</strong>,代码如下：<em>（在旅拍页面就有使用到它，为了让tabBar 和 tabBarView在切换时不重新加载）</em></p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">TravelTabPage</span> <span class="keyword">extends</span> <span class="title">StatefulWidget</span> </span>&#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="comment">//需要重写 wantKeepAlive 且 设置成 true</span></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">bool</span> <span class="keyword">get</span> wantKeepAlive =&gt; <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="with-SingleTickerProviderStateMixin"><a href="#with-SingleTickerProviderStateMixin" class="headerlink" title="with SingleTickerProviderStateMixin"></a>with SingleTickerProviderStateMixin</h3><p>混入 <code>SingleTickerProviderStateMixin</code> 可以在切换页面时候，使用动画效果，如：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">bottomNavigationBar: BottomNavigationBar(</span><br><span class="line">          currentIndex: _currentIndex,</span><br><span class="line">          onTap: (index) &#123;</span><br><span class="line">            _controller.animateToPage( <span class="comment">// _controller 是 PageView 的 PageController</span></span><br><span class="line">                index,</span><br><span class="line">                curve: Curves.easeIn, duration: <span class="built_in">Duration</span>(milliseconds: <span class="number">260</span>)</span><br><span class="line">            );</span><br><span class="line">            setState(() &#123;</span><br><span class="line">              _currentIndex = index;</span><br><span class="line">            &#125;);</span><br><span class="line">          &#125;,</span><br><span class="line">          ...</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="SystemUiOverlayStyle"><a href="#SystemUiOverlayStyle" class="headerlink" title="SystemUiOverlayStyle"></a>SystemUiOverlayStyle</h3><p><code>SystemUiOverlayStyle</code> 可实现状态栏沉浸式，设置全局配置，如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> main() =&gt; runApp(MyApp());</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyApp</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    TextStyle textStyle = TextStyle(fontSize: <span class="number">20</span>);</span><br><span class="line">    SystemUiOverlayStyle style = SystemUiOverlayStyle(</span><br><span class="line">        statusBarColor: Colors.transparent,</span><br><span class="line">        statusBarIconBrightness: Brightness.light</span><br><span class="line">    );</span><br><span class="line">    SystemChrome.setSystemUIOverlayStyle(style);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> MaterialApp(</span><br><span class="line">      ...</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>单页面设置如下：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="class"><span class="keyword">class</span> <span class="title">_HomePageState</span> <span class="keyword">extends</span> <span class="title">State</span>&lt;<span class="title">HomePage</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light); <span class="comment">//设置状态栏沉浸式</span></span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      backgroundColor: Color(<span class="number">0xfffafafc</span>),</span><br><span class="line">      body: LoadingContainer(</span><br><span class="line">        ...</span><br><span class="line">      )</span><br><span class="line">      ...</span><br></pre></td></tr></table></figure><p>暂时只能想到这些常用的知识点，以后如有新的会慢慢补充。</p><p><strong>项目GitHub地址:</strong> <a href="https://github.com/persilee/flutter_ctrip" target="_blank" rel="noopener">https://github.com/persilee/flutter_ctrip</a></p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/flutterCtrip.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>三招让你成为程序猿中优秀的射鸡湿</title>
      <link>https://h.lishaoy.net/goodDesigner.html</link>
      <guid>https://h.lishaoy.net/goodDesigner.html</guid>
      <pubDate>Fri, 01 Mar 2019 08:24:32 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/goodDesigner/goodDesigner.jpg&quot; class=&quot;full-image&quot; alt=&quot;goodDesigner&quot; title=&quot;goodDesigner&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;这篇文章总结一下之前项目中一些 &lt;strong&gt;前端&lt;/strong&gt; 工具及技巧，主要包括 &lt;strong&gt;Iconfont的正确使用姿势&lt;/strong&gt; 、 &lt;strong&gt;如何使用酷炫漂亮的动画（Lottie）&lt;/strong&gt; 、 &lt;strong&gt;如何加入页面滚动入场离场动效&lt;/strong&gt;。&lt;/p&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/goodDesigner/goodDesigner.jpg" class="full-image" alt="goodDesigner" title="goodDesigner"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>这篇文章总结一下之前项目中一些 <strong>前端</strong> 工具及技巧，主要包括 <strong>Iconfont的正确使用姿势</strong> 、 <strong>如何使用酷炫漂亮的动画（Lottie）</strong> 、 <strong>如何加入页面滚动入场离场动效</strong>。</p><a id="more"></a><h2 id="Iconfont的正确使用姿势"><a href="#Iconfont的正确使用姿势" class="headerlink" title="Iconfont的正确使用姿势"></a>Iconfont的正确使用姿势</h2><p><a href="https://www.iconfont.cn" target="_blank" rel="noopener">Iconfont</a> 是阿里巴巴打造的矢量图标库，图标丰富多彩（单色和彩色），使用方便快捷（可筛选图片创建自己项目图标库），支持在线使用，摆脱了传统的图片的繁琐和css字体图标库引入的冗余。</p><p>下面介绍下如何使用 Iconfont</p><h3 id="搜索选择图标"><a href="#搜索选择图标" class="headerlink" title="搜索选择图标"></a>搜索选择图标</h3><p>点击 <a href="https://www.iconfont.cn" target="_blank" rel="noopener">Iconfont</a> 打开页面如图，可以搜索 🔍 关键字，找到想要的图标</p><p><img src="https://cdn.lishaoy.net/goodDesigner/Iconfont.png" alt="Iconfont" title="Iconfont"></p><p>例如，搜索 ‘image’ 关键字，如图</p><p><img src="https://cdn.lishaoy.net/goodDesigner/Iconfont2.png" alt="Iconfont" title="Iconfont"></p><p>鼠标放到图标上会出现 <strong>添加入库</strong> 、 <strong>收藏</strong> 、 <strong>下载图标</strong> 选项，一般我会选择 <strong>添加入库</strong> ，之后统一添加到项目，生成在线地址引入项目（后面会介绍到）</p><p>右边的蓝色皇冠按钮可以进行 <em>精选</em> 、 <em>全部</em> 的筛选，红色的按钮可以进行 <em>单色</em> 、 <em>多色</em> 、 <em>全部</em> 的筛选</p><p>点击 下载 会弹出下载页面，可以进行图标的编辑和不同格式的下载，如图</p><p><img src="https://cdn.lishaoy.net/goodDesigner/Iconfont3.1.png" alt="Iconfont" title="Iconfont"></p><h3 id="添加入库生成在线连接"><a href="#添加入库生成在线连接" class="headerlink" title="添加入库生成在线连接"></a>添加入库生成在线连接</h3><p>选择 <strong>添加入库</strong> 的图标，会在右上角的购物车显示数量，点击购物车图标，会弹出右侧栏，如图</p><p><img src="https://cdn.lishaoy.net/goodDesigner/Iconfont4.png" alt="Iconfont" title="Iconfont"></p><p>可以批量下载和添加到项目，点击 <strong>添加至项目</strong> 如图</p><p><img src="https://cdn.lishaoy.net/goodDesigner/Iconfont5.png" alt="Iconfont" title="Iconfont"></p><p>给项目取一个名字，点击确定，如图</p><p><img src="https://cdn.lishaoy.net/goodDesigner/Iconfont6.png" alt="Iconfont" title="Iconfont"></p><p>可以看到有三种图标引入的方式，默认选中的是 <strong>Font class</strong> 的方式，也推荐使用这种方式</p><p><strong>Unicode</strong> : 是以字体的方式引入，如下</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">font-face</span> &#123;</span><br><span class="line">  <span class="attribute">font-family</span>: <span class="string">'iconfont'</span>;  <span class="comment">/* project id 1066942 */</span></span><br><span class="line">  <span class="attribute">src</span>: <span class="built_in">url</span>(<span class="string">'//at.alicdn.com/t/font_1066942_yvi703p2pv.eot'</span>);</span><br><span class="line">  <span class="attribute">src</span>: <span class="built_in">url</span>(<span class="string">'//at.alicdn.com/t/font_1066942_yvi703p2pv.eot?#iefix'</span>) <span class="built_in">format</span>(<span class="string">'embedded-opentype'</span>),</span><br><span class="line">  <span class="built_in">url</span>(<span class="string">'//at.alicdn.com/t/font_1066942_yvi703p2pv.woff2'</span>) <span class="built_in">format</span>(<span class="string">'woff2'</span>),</span><br><span class="line">  <span class="built_in">url</span>(<span class="string">'//at.alicdn.com/t/font_1066942_yvi703p2pv.woff'</span>) <span class="built_in">format</span>(<span class="string">'woff'</span>),</span><br><span class="line">  <span class="built_in">url</span>(<span class="string">'//at.alicdn.com/t/font_1066942_yvi703p2pv.ttf'</span>) <span class="built_in">format</span>(<span class="string">'truetype'</span>),</span><br><span class="line">  <span class="built_in">url</span>(<span class="string">'//at.alicdn.com/t/font_1066942_yvi703p2pv.svg#iconfont'</span>) <span class="built_in">format</span>(<span class="string">'svg'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Font class</strong> : 是以Css的方式引入，如下</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">//at.alicdn.com/t/font_1066942_yvi703p2pv.css</span><br></pre></td></tr></table></figure><p><strong>Symbol</strong> : 是以js的方式引入，如下</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">//at.alicdn.com/t/font_1066942_yvi703p2pv.js</span><br></pre></td></tr></table></figure><p>如想了解3中方式具体用法，可点击 <a href="https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.d8cf4382a&amp;helptype=code" target="_blank" rel="noopener">官方文档</a>，这里我主要介绍 <strong>Font class</strong> 的方式</p><h3 id="引入项目使用"><a href="#引入项目使用" class="headerlink" title="引入项目使用"></a>引入项目使用</h3><p>首先，把生成的链接引入到页面中，如下</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;link rel="stylesheet" href="//at.alicdn.com/t/font_1066942_yvi703p2pv.css"&gt;</span><br></pre></td></tr></table></figure><p>其次，用 <code>&lt;i class=&quot;iconfont icon-Userselect&quot;&gt;&lt;/i&gt;</code> 在页面中使用，大小颜色都可以用 <code>css</code> 调整</p><p>阿里在代码的复制、图标的搜索、编辑、下载、筛选等一些操作上做的很到位，使用起来方便快捷</p><p>我在之前的项目里也使用过，还是很漂亮的 <a href="https://a.lishaoy.net" target="_blank" rel="noopener">https://a.lishaoy.net</a> ，如图是我在项目里使用的一些图标，每个图标是不是都做的很精致</p><p><img src="https://cdn.lishaoy.net/goodDesigner/Iconfont7.png" alt="Iconfont" title="Iconfont"></p><h2 id="Lottie开源动画库"><a href="#Lottie开源动画库" class="headerlink" title="Lottie开源动画库"></a>Lottie开源动画库</h2><p><strong>Lottie</strong> 是Airbnb开源的一个面向 iOS、Android、React Native 的动画库，能够直接把 <strong>AE</strong> 导出的动画文件（json），引入到页面使用，以下是官方给出的效果图</p><p><img src="https://cdn.lishaoy.net/goodDesigner/lottie.gif" alt="no-shadow" title="Lottie"></p><p><img src="https://cdn.lishaoy.net/goodDesigner/lottie2.gif" alt="no-shadow" title="Lottie"></p><p><strong>Lottie</strong> 支持 iOS、Android、React Native 、Web ，这里主要介绍 lottie-Web 是如何使用，更多使用方法可以参考 <a href="http://airbnb.io/lottie/" target="_blank" rel="noopener">http://airbnb.io/lottie/</a></p><p>首先，在页面中引入 <code>CDN</code> 上的文件，如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;script src=<span class="string">"https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.4.2/lottie.min.js"</span> type=<span class="string">"text/javascript"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure><p>其次，使用 <strong>AE</strong> 制作动画（UI提供或者自己制作）导出的 <code>json</code>，或者可以在 <a href="https://lottiefiles.com/featured" target="_blank" rel="noopener">Lottie Files</a> 下载 （<a href="https://lottiefiles.com/featured" target="_blank" rel="noopener">Lottie Files</a> 是一个拥有高质量 <strong>Lottie</strong> 文件格式动画的网站，不仅设计师可以在上面陈列他们的动画而且还提供免费下载）</p><p>以下是我在项目里使用的效果图（上传图片中会加重动画，上传成功动画停止），具体效果可以去我的项目上传图片体验 <a href="https://a.lishaoy.net/posts/56" target="_blank" rel="noopener">上传图片动画效果</a></p><p><img src="https://cdn.lishaoy.net/adonisjs/image_upload4.gif" alt="Lottie" title="Lottie"></p><p>具体的代码如下，在页面中创建需要加重动画的容器（<code>HTML</code> 代码）</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"image-load d-flex justify-content-center align-items-center"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"box"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"lottie"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"text text-muted text-center"</span>&gt;</span>The picture is being uploaded ...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然后，用 <code>js</code> 初始化动画，如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">let</span> anim = lottie.loadAnimation(&#123;</span><br><span class="line">  container: $(<span class="string">'.image-load .lottie'</span>)[<span class="number">0</span>], <span class="comment">//动画容器的元素</span></span><br><span class="line">  renderer: <span class="string">'svg'</span>, <span class="comment">//支持 svg 和 canvas</span></span><br><span class="line">  loop: <span class="literal">true</span>, <span class="comment">//是否循环</span></span><br><span class="line">  autoplay: <span class="literal">false</span>, <span class="comment">// 是否自动播放</span></span><br><span class="line">  path: <span class="string">'/EmojiReaction.json'</span> <span class="comment">//动画json文件的位置</span></span><br><span class="line">&#125;)</span><br><span class="line">anim.addEventListener(<span class="string">'loopComplete'</span>, () =&gt; &#123;  <span class="comment">// 监听 `loopComplete` 事件，每次播放完成执行</span></span><br><span class="line">  anim.pause() <span class="comment">//停止播放</span></span><br><span class="line">  $(<span class="string">'.image-load'</span>).removeClass(<span class="string">'loading'</span>) <span class="comment">//隐藏容器</span></span><br><span class="line">  $(<span class="string">'.image-load .box .text'</span>).text(<span class="string">'The picture is being uploaded...'</span>).removeClass(<span class="string">'text-success'</span>).addClass(<span class="string">'text-muted'</span>) <span class="comment">//改变说明文字状态及颜色</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>更多的参数和事件可查阅官方文档 <a href="http://airbnb.io/lottie/web/web.html" target="_blank" rel="noopener">Lottie-Web</a></p><h2 id="如何加入页面滚动入场离场动效"><a href="#如何加入页面滚动入场离场动效" class="headerlink" title="如何加入页面滚动入场离场动效"></a>如何加入页面滚动入场离场动效</h2><p>首先，让我们来先看看效果，如图（效果来源我的博客 <a href="https://lishaoy.net/laboratory/" target="_blank" rel="noopener">https://lishaoy.net</a>）</p><p><img src="https://cdn.lishaoy.net/goodDesigner/lottie3.gif" alt="Lottie" title="Lottie"></p><p>以上动效就是用的 <strong>AOS</strong> 这个库，具体的使用方法也很简单</p><p>在页面上引入 <code>css</code> 和 <code>js</code> 文件</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"https://unpkg.com/aos@next/dist/aos.css"</span>/&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://unpkg.com/aos@next/dist/aos.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然后，用 <code>AOS.init()</code> 初始化，这样初始化，使用的是默认设置，具体有很多参数可以调整，详情可查阅 <a href="https://github.com/michalsnik/aos" target="_blank" rel="noopener">项目文档</a>，如</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">AOS.init(&#123;</span><br><span class="line">  <span class="comment">// Global settings:</span></span><br><span class="line">  disable: <span class="literal">false</span>, <span class="comment">// accepts following values: 'phone', 'tablet', 'mobile', boolean, expression or function</span></span><br><span class="line">  startEvent: <span class="string">'DOMContentLoaded'</span>, <span class="comment">// name of the event dispatched on the document, that AOS should initialize on</span></span><br><span class="line">  initClassName: <span class="string">'aos-init'</span>, <span class="comment">// class applied after initialization</span></span><br><span class="line">  animatedClassName: <span class="string">'aos-animate'</span>, <span class="comment">// class applied on animation</span></span><br><span class="line">  useClassNames: <span class="literal">false</span>, <span class="comment">// if true, will add content of `data-aos` as classes on scroll</span></span><br><span class="line">  disableMutationObserver: <span class="literal">false</span>, <span class="comment">// disables automatic mutations' detections (advanced)</span></span><br><span class="line">  debounceDelay: <span class="number">50</span>, <span class="comment">// the delay on debounce used while resizing window (advanced)</span></span><br><span class="line">  throttleDelay: <span class="number">99</span>, <span class="comment">// the delay on throttle used while scrolling the page (advanced)</span></span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">  <span class="comment">// Settings that can be overridden on per-element basis, by `data-aos-*` attributes:</span></span><br><span class="line">  offset: <span class="number">120</span>, <span class="comment">// offset (in px) from the original trigger point</span></span><br><span class="line">  delay: <span class="number">0</span>, <span class="comment">// values from 0 to 3000, with step 50ms</span></span><br><span class="line">  duration: <span class="number">400</span>, <span class="comment">// values from 0 to 3000, with step 50ms</span></span><br><span class="line">  easing: <span class="string">'ease'</span>, <span class="comment">// default easing for AOS animations</span></span><br><span class="line">  once: <span class="literal">false</span>, <span class="comment">// whether animation should happen only once - while scrolling down</span></span><br><span class="line">  mirror: <span class="literal">false</span>, <span class="comment">// whether elements should animate out while scrolling past them</span></span><br><span class="line">  anchorPlacement: <span class="string">'top-bottom'</span>, <span class="comment">// defines which position of the element regarding to window should trigger the animation</span></span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>最后，在页面上使用即可，如</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">data-aos</span>=<span class="string">"fade-in"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>或者，也可以单独给元素设置参数，使用 <code>data-aos-*</code> ，如</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">data-aos</span>=<span class="string">"fade-in"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">data-aos-offset</span>=<span class="string">"200"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">data-aos-delay</span>=<span class="string">"50"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">data-aos-duration</span>=<span class="string">"1000"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">data-aos-easing</span>=<span class="string">"ease-in-out"</span></span></span><br><span class="line"><span class="tag">&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>更多的载入效果可以参考官方给出的 <a href="http://michalsnik.github.io/aos/" target="_blank" rel="noopener">示例</a></p><p>如在您做的页面或应用中使用了这三招，<strong>精致小图标</strong> 、 <strong>炫酷的动画</strong> 、 <strong>页面滚动载入动效</strong> ，那么瞬间让您的页面或应用高大上且生动活泼起来。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/goodDesigner.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>用 Node.js 快速开发出多功能的多人在线的文章分享平台</title>
      <link>https://h.lishaoy.net/adonisjs.html</link>
      <guid>https://h.lishaoy.net/adonisjs.html</guid>
      <pubDate>Sun, 16 Dec 2018 18:03:27 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/adonisjs/adonisjs1.png&quot; class=&quot;full-image&quot; alt=&quot;Adonisjs&quot; title=&quot;Adonisjs&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;最近在学习使用 &lt;code&gt;Node.js&lt;/code&gt; 框架，边学习边使用，花了大概 &lt;strong&gt;3周&lt;/strong&gt; 时间做完这个 &lt;strong&gt;Web应用&lt;/strong&gt; 且在 &lt;time&gt;12月16&lt;/time&gt; 凌晨左右上线成功（其实就是把开发环境搬到服务器）， 地址： &lt;a href=&quot;https://a.lishaoy.net&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://a.lishaoy.net&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;这个 &lt;strong&gt;Web应用&lt;/strong&gt; 的代码是开源的，如对这个应用感兴趣，想知道代码是如何运行的，可以去我 &lt;strong&gt;GitHub&lt;/strong&gt; 下载或 &lt;code&gt;clone&lt;/code&gt; ：&lt;a href=&quot;https://github.com/persilee/adonis_pro&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;应用源码&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/adonisjs/adonisjs1.png" class="full-image" alt="Adonisjs" title="Adonisjs"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>最近在学习使用 <code>Node.js</code> 框架，边学习边使用，花了大概 <strong>3周</strong> 时间做完这个 <strong>Web应用</strong> 且在 <time>12月16</time> 凌晨左右上线成功（其实就是把开发环境搬到服务器）， 地址： <a href="https://a.lishaoy.net" target="_blank" rel="noopener">https://a.lishaoy.net</a>  </p><p>这个 <strong>Web应用</strong> 的代码是开源的，如对这个应用感兴趣，想知道代码是如何运行的，可以去我 <strong>GitHub</strong> 下载或 <code>clone</code> ：<a href="https://github.com/persilee/adonis_pro" target="_blank" rel="noopener">应用源码</a></p><hr><a id="more"></a><p>首先，来看看用 <strong>3周</strong> 时间做出来的应用都有些什么功能，之后再看看选用的 <code>Node.js</code> 框架，最后看看 <code>Node.js</code> 项目如何部署到服务器。</p><h2 id="Web应用功能"><a href="#Web应用功能" class="headerlink" title="Web应用功能"></a>Web应用功能</h2><h3 id="登录、注册验证"><a href="#登录、注册验证" class="headerlink" title="登录、注册验证"></a>登录、注册验证</h3><p><strong>登录功能</strong></p><ul><li>输入框没有输入点击登录会提示：用户名、密码不能为空</li><li>输入的用户错误或不存在会提示：用户不存在</li><li>输入的密码错误会提示：密码错误</li><li>登录后会重定向到用户上次访问的地址</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/login.gif" alt="no-shadow" title="Login"></p><p><strong>注册功能</strong></p><ul><li>输入框没有输入点击注册会提示：用户名、邮箱、密码不能为空</li><li>用户名和邮箱与其他用户相同会提示：用户名、邮箱已存在</li><li>密码小于6位数会提示：最小长度是6位</li><li>注册成功后会发送验证邮件到用户邮箱，需点击邮箱按钮验证</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/register.gif" alt="no-shadow" title="Register"></p><h3 id="文章列表"><a href="#文章列表" class="headerlink" title="文章列表"></a>文章列表</h3><p>登录进来，会显示文章列表页面，显示内容如下：</p><ul><li>文章标题：点击可进入文章详情页</li><li>作者头像、作者名称：点击可进入作者信息页</li><li>时间：显示创建时间（多久以前方式显示）</li><li>阅读次数、点赞次数</li><li>文章简要：自动摘取章头文章</li><li>缩略图：自动摘取文章第一张图片</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/posts.png" alt="no-shadow" title="Post List"></p><h3 id="文章详情"><a href="#文章详情" class="headerlink" title="文章详情"></a>文章详情</h3><p>点击文章标题可进入文章详情页面，内容如下：</p><ul><li>文章标题</li><li>作者头像、作者名称</li><li>发布时间</li><li>阅读次数和点赞次数</li><li>编辑按钮（仅作者可见）</li><li>左侧浮动工具栏（点赞、发送邮件到自己邮箱、返回顶部、分享）</li><li>点赞：文章被点赞后，作者可以收到消息通知，且将文章收录到点赞列表（支持匿名点赞，但不会记录通知，只会加点赞数）</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/post.png" alt="no-shadow" title="Post"></p><h3 id="编辑文章支持-Markdown"><a href="#编辑文章支持-Markdown" class="headerlink" title="编辑文章支持 Markdown"></a>编辑文章支持 Markdown</h3><p>新建文章和修改文章都支持 <strong>Markdown</strong> 语法，且会每隔6秒钟自动保存</p><p><img src="https://cdn.lishaoy.net/adonisjs/post_edit.png" alt="no-shadow" title="Post Edit"></p><h3 id="个人信息"><a href="#个人信息" class="headerlink" title="个人信息"></a>个人信息</h3><p>个人信息页面显示内容如下</p><ul><li>作者的头像、姓名、简介（支持emoji）</li><li>信息栏：GitHub 链接、个人网站链接、发布文章数、总阅读次数、总点赞次数</li><li>发布文章列表：个人发布的所有文章(有删除和编辑按钮)</li><li>已赞文章列表：点过赞的文章会记录在这里</li><li>关注者列表：关注你的用户（关注过的用户，关注按钮高亮显示）</li><li>已关注列表：你关注的用户（关注过的用户，关注按钮高亮显示)</li><li>关注按钮：作者本人不可见，点击可关注，再次点击取消关注，关注后，用户会收到消息通知</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/profile.gif" alt="no-shadow" title="Profile"></p><p>文章删除编辑快捷入口，如图</p><p><img src="https://cdn.lishaoy.net/adonisjs/edit_delete_post.gif" alt="no-shadow" title="Edit &amp;&amp; Delete post"></p><p>下面是我用另一个用户登录，进入到个人信息页面就会显示关注按钮，如图</p><p><img src="https://cdn.lishaoy.net/adonisjs/follow.png" alt="no-shadow" title="Follow"></p><h3 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h3><p>点击文件上传小图标可进入文件上传页面，点击 <strong>Files</strong> 链接可进入文件上传列表，显示内容如图：</p><p><img src="https://cdn.lishaoy.net/adonisjs/file_upload.png" alt="no-shadow" title="File Upload"></p><p><img src="https://cdn.lishaoy.net/adonisjs/file_list.png" alt="no-shadow" title="File List"></p><h3 id="文件预览和编辑"><a href="#文件预览和编辑" class="headerlink" title="文件预览和编辑"></a>文件预览和编辑</h3><p>从文件列表页面点击标题可进入文件预览页面，显示内容如下：</p><ul><li>如果是图片显示图片，如果是视频显示视频</li><li>工具栏：发送邮件到自己邮箱（登录可见）、编辑按钮、删除按钮（登录自己上传可见）</li><li>文件名称</li><li>下载按钮</li><li>上传者头像</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/file_show.gif" alt="no-shadow" title="File Show"></p><h3 id="消息通知"><a href="#消息通知" class="headerlink" title="消息通知"></a>消息通知</h3><p>点击铃铛小图标可进入消息通知页面，内容如下：</p><ul><li>点赞消息列表：收到用户点赞通知，最新的未读消息会高亮显示，点击点赞者头像进入个人信息页面，点击文章标题进入你的文章详情页面</li><li>关注者列表：收到关注者的通知，最新未读消息会高亮显示，点关注按钮也可关注他，再点击取消关注</li><li>系统消息：目前还没有做功能实现</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/notification.gif" alt="no-shadow" title="Notification"></p><h3 id="工具栏列表"><a href="#工具栏列表" class="headerlink" title="工具栏列表"></a>工具栏列表</h3><p>点击个人头像可展开工具栏列表，内容如下：</p><ul><li>写文章：点击可新建文章编辑页面，和 ➕ 小图标是同样功能</li><li>上传文件：点击可打开文件上传页面，和上传小图标是同样功能</li><li>个人信息： 点击可进入个人信息页面</li><li>已赞：点击可查看已赞过得文章</li><li>设置：点击可打开个人设置页面</li><li>登出：点击退出登录</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/tool_menu.png" alt="no-shadow" title="Tool Menu"></p><h3 id="设置"><a href="#设置" class="headerlink" title="设置"></a>设置</h3><p>点击工具栏上的设置按钮可以设置页面，内容如下：</p><p><strong>个人信息设置</strong></p><ul><li>头像：头像是使用的 <code>Gravatar</code> 提供的功能，根据邮箱生成头像</li><li>用户名</li><li>邮箱：已验证通过会显示验证小图标，没有通过的会显示提示</li><li>GitHub：只需填写有户名</li><li>个人简介：支持emoji</li><li>个人网站</li></ul><p><img src="https://cdn.lishaoy.net/setting_profile.png" alt="no-shadow" title="Setting Profile"></p><p><strong>修改密码设置</strong></p><p>需填写原密码，新密码，再次输入密码</p><p><img src="https://cdn.lishaoy.net/setting_pwd.png" alt="no-shadow" title="Setting Password"></p><h3 id="聊天室"><a href="#聊天室" class="headerlink" title="聊天室"></a>聊天室</h3><p>点击 <strong>Chatroom</strong> 链接可进入聊天室，当然这个是用的 <code>websocket</code> 做的，内容如下：</p><ul><li>状态图标：显示链接状态</li><li>活动用户：左侧黑色区域会动态显示活动用户</li><li>消息：会显示发送消息，进入、离开房间通知消息（支持匿名发送消息，但不会保存消息）</li><li>消息输入：消息输入框可输入消息，<kbd>Cmd</kbd> — <kbd>Enter</kbd> 换行（Windows会显示提示Ctrl+Enter），回车发送消息</li></ul><p><img src="https://cdn.lishaoy.net/adonisjs/chatroom1.gif" alt="no-shadow" title="Chart Room"></p><p>加入房间和离开房间都有消息通知，如图</p><p><img src="https://cdn.lishaoy.net/adonisjs/chatroom.png" alt="no-shadow" title="Chart Room"></p><h2 id="Node-js-框架"><a href="#Node-js-框架" class="headerlink" title="Node.js 框架"></a>Node.js 框架</h2><p>这个应用的开发我选择的是 <code>Adonisjs</code> 框架，他和 <code>PHP</code> 的 <code>Laravel</code> 有些像，<code>Adonisjs</code> 是在操作系统上运行的 <code>Node.js</code> <strong>MVC</strong> 框架。</p><p>接下来，来看看 <code>Adonisjs</code> 框架有哪些特性：</p><h3 id="环境安装简单"><a href="#环境安装简单" class="headerlink" title="环境安装简单"></a>环境安装简单</h3><p>不管是开发环境还是生产环境，安装 <code>Adonisjs</code> 运行环境都是非常简单，先来看看开发环境的安装，生产环境后面会提到。</p><p>首先，我们的电脑上需要安装好 <code>Node.js</code>大于 <em>8.00</em> 版本，管理 <code>Node.js</code> 可以使用 <code>nvm</code></p><p>其次，就可以使用 <code>npm</code> 安装 <code>Adonis CLI</code> 命令行工具（管理 <code>npm</code> 使用源可以使用 <code>nrm</code>）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i -g @adonisjs/cli</span><br></pre></td></tr></table></figure><p>这样就可以在全局使用 <code>adonis</code> 命令</p><p>再次，可以是 <code>adonis new</code> 命令创建项目</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adonis new adonis_pro</span><br></pre></td></tr></table></figure><p>在 <code>cd</code> 进入项目，执行 <code>adonis serve --dev</code> 运行项目</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> adonis_pro</span><br><span class="line">adonis serve --dev</span><br></pre></td></tr></table></figure><p>这样您的开发环境就搭建完成。</p><h3 id="RMVC"><a href="#RMVC" class="headerlink" title="RMVC"></a>RMVC</h3><p><code>RMVC</code> 就是路由、模型、视图、控制器。</p><h4 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h4><p>创建一条路由非常简单，如 </p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Route.get(<span class="string">'liked/:userId/:postId'</span>, <span class="string">'LikedController.liked'</span>)</span><br></pre></td></tr></table></figure><p>这条路由就是用来处理上面提到的点赞功能的</p><p>当然，<code>Adonisjs</code> 提供了 <strong>资源路由</strong> 以便您更方便的创建路由，例如</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">Route.resource(<span class="string">'posts'</span>, <span class="string">'PostController'</span>).middleware(</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Map</span>([</span><br><span class="line">[ [ <span class="string">'create'</span>, <span class="string">'store'</span>, <span class="string">'edit'</span>, <span class="string">'update'</span>, <span class="string">'destroy'</span> ], [ <span class="string">'auth'</span> ] ],</span><br><span class="line">[ [ <span class="string">'update'</span>, <span class="string">'destroy'</span>, <span class="string">'edit'</span> ], [ <span class="string">'own:post'</span> ] ]</span><br><span class="line">])</span><br><span class="line">).validator(<span class="keyword">new</span> <span class="built_in">Map</span>([</span><br><span class="line">  [[<span class="string">'posts.update'</span>, <span class="string">'posts.store'</span>], [<span class="string">'StorePost'</span>]]</span><br><span class="line">]))</span><br></pre></td></tr></table></figure><p>这个路由是来处理上面应用提到的文章的 <em>增、删、改、查</em> ，这个可能有些复杂，使用了 <strong>中间件</strong> 来处理用户登录状态和操作权限，使用了 <strong>验证器</strong> 来处理表单验证，这里不介绍的太复杂，如想了解这些具体功能，可以需要花点时间了解学习。</p><p>我们可以去掉 <strong>中间件</strong> 和 <strong>验证器</strong> ，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Route.resource(<span class="string">'posts'</span>, <span class="string">'PostController'</span>)</span><br></pre></td></tr></table></figure><p>这条资源路由，其实就包含了以下路由：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">Route.get(url, closure)</span><br><span class="line">Route.post(url, closure)</span><br><span class="line">Route.put(url, closure)</span><br><span class="line">Route.patch(url, closure)</span><br><span class="line">Route.delete(url, closure)</span><br></pre></td></tr></table></figure><p><code>Adonisjs</code> 还提供了路由组和其他一些功能，路由组如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">Route.group(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">Route.get(<span class="string">'profile'</span>, <span class="string">'ProfileController.edit'</span>).as(<span class="string">'profile.edit'</span>)</span><br><span class="line">Route.post(<span class="string">'profile'</span>, <span class="string">'ProfileController.update'</span>).as(<span class="string">'profile.update'</span>).validator(<span class="string">'UpdateProfile'</span>)</span><br><span class="line">Route.get(<span class="string">'password'</span>, <span class="string">'PasswordController.edit'</span>).as(<span class="string">'password.edit'</span>)</span><br><span class="line">Route.post(<span class="string">'password'</span>, <span class="string">'PasswordController.update'</span>).as(<span class="string">'password.update'</span>).validator(<span class="string">'UpdatePassword'</span>)</span><br><span class="line">&#125;)</span><br><span class="line">.prefix(<span class="string">'settings'</span>)</span><br><span class="line">.middleware([ <span class="string">'auth'</span> ])</span><br></pre></td></tr></table></figure><p>使用 <code>.prefix</code> 和 <code>Route.group</code> 来创建路由组，这条路由组是处理 个人信息设置 功能的，这样访问页面是就统一要带上 <code>settings/**</code> 。</p><h4 id="控制器"><a href="#控制器" class="headerlink" title="控制器"></a>控制器</h4><p><code>Adonisjs</code> 提供了命令行来创建控制器，如</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adonis make:controller User --<span class="built_in">type</span> http</span><br></pre></td></tr></table></figure><p>这样就创建了一个 <code>User</code> 控制器,自动生成代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserController</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = UserController</span><br></pre></td></tr></table></figure><p>当然，我们还可以使用 <code>--resource</code> 创建资源类型的控制器</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adonis make:controller Post --resource</span><br></pre></td></tr></table></figure><p>自动生成代码，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostController</span> </span>&#123;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Show a list of all posts.</span></span><br><span class="line"><span class="comment">   * GET posts</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"><span class="keyword">async</span> index (&#123; request, response, view &#125;) &#123;&#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Render a form to be used for creating a new posts.</span></span><br><span class="line"><span class="comment">   * GET posts/create</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"><span class="keyword">async</span> create (&#123; request, response, view &#125;) &#123;&#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Create/save a new posts.</span></span><br><span class="line"><span class="comment">   * POST posts</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"><span class="keyword">async</span> store (&#123; request, response, view &#125;) &#123;&#125;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Display a single posts.</span></span><br><span class="line"><span class="comment">   * GET posts/:id</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"><span class="keyword">async</span> show (&#123; request, response, view &#125;) &#123;&#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Render a form to update an existing posts.</span></span><br><span class="line"><span class="comment">   * GET posts/:id/edit</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"><span class="keyword">async</span> edit (&#123; request, response, view &#125;) &#123;&#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Update posts details.</span></span><br><span class="line"><span class="comment">   * PUT or PATCH posts/:id</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"><span class="keyword">async</span> update (&#123; request, response, view&#125;) &#123;&#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Delete a posts with id.</span></span><br><span class="line"><span class="comment">   * DELETE posts/:id</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"><span class="keyword">async</span> destroy (&#123; params, request, response &#125;) &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = PostController</span><br></pre></td></tr></table></figure><p>和上面的资源路由是对应的，如用 <code>GET</code> 请求访问 <em>posts</em> 就会调用 <code>index</code> 方法（一般用来显示） ，再如：用 <code>DELETE</code> 请求访问 <em>posts/1</em> 就会执行 <code>destroy</code> 方法（一般用来删除）。</p><h4 id="模型"><a href="#模型" class="headerlink" title="模型"></a>模型</h4><p><code>Adonisjs</code> 提供了两种模式来处理数据，<code>Query builder</code> 和 <code>LUCID</code></p><p>首先，我们可以通过 <code>adonis make:migration</code> 来创建数据表</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adonis make:migration users</span><br></pre></td></tr></table></figure><p>会自动生成代码，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Schema = use(<span class="string">'Schema'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UsersSchema</span> <span class="keyword">extends</span> <span class="title">Schema</span> </span>&#123;</span><br><span class="line">  up () &#123;</span><br><span class="line">    <span class="keyword">this</span>.create(<span class="string">'users'</span>, (table) =&gt; &#123;</span><br><span class="line">      table.increments()</span><br><span class="line">      table.timestamps()</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  down () &#123;</span><br><span class="line">    <span class="keyword">this</span>.drop(<span class="string">'users'</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="built_in">module</span>.exports = UsersSchema</span><br></pre></td></tr></table></figure><p>这是我们只需在其中添加想要的字段就行，如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="meta">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Schema = use(<span class="string">'Schema'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UsersSchema</span> <span class="keyword">extends</span> <span class="title">Schema</span> </span>&#123;</span><br><span class="line">  up () &#123;</span><br><span class="line">    <span class="keyword">this</span>.create(<span class="string">'users'</span>, (table) =&gt; &#123;</span><br><span class="line">      table.increments()</span><br><span class="line">      table.string(<span class="string">'username'</span>, <span class="number">80</span>).notNullable().unique()</span><br><span class="line">      table.string(<span class="string">'email'</span>, <span class="number">254</span>).notNullable().unique()</span><br><span class="line">      table.string(<span class="string">'password'</span>, <span class="number">60</span>).notNullable()</span><br><span class="line">      table.timestamps()</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  down () &#123;</span><br><span class="line">    <span class="keyword">this</span>.drop(<span class="string">'users'</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="built_in">module</span>.exports = UsersSchema</span><br></pre></td></tr></table></figure><p>在执行 <code>adonis migration:run</code> 命令就可以在数据库生成数据表</p><p>再来看看，如何获取数据，可以使用 <code>Query builder</code> 和 <code>LUCID</code> 两种方式</p><p>先来看看 <code>Query builder</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">const</span> Database = use(<span class="string">'Database'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">async</span> index (request, response) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> Database</span><br><span class="line">      .table(<span class="string">'users'</span>)</span><br><span class="line">      .where(<span class="string">'username'</span>, <span class="string">'admin'</span>)</span><br><span class="line">      .first()</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>user</code> 表 <code>name</code> 是 <code>admin</code> 的用户</p><p><code>Adonisjs</code> 提供了非常多的方法去操作数据，不是特复杂的关系都够用，如果，关系比较复杂，还可以用原生的 <code>sql</code> 操作，如</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Database = use(<span class="string">'Database'</span>)</span><br><span class="line"></span><br><span class="line">  <span class="class"><span class="keyword">class</span> <span class="title">NotificationController</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> followNotice (&#123; auth, view &#125;) &#123;</span><br><span class="line">    <span class="keyword">const</span> notices = <span class="keyword">await</span> Database.raw(<span class="string">'select users.id as user_id,users.username,users.email,b.title,b.created_at,b.is_read,b.id as post_id from adonis.users , (select posts.id,posts.title, a.user_id,a.created_at,a.is_read from adonis.posts,(SELECT post_user.post_id, post_user.user_id, post_user.created_at, post_user.is_read FROM adonis.post_user where post_user.post_id in (SELECT posts.id FROM adonis.posts where user_id = ?)) as a where posts.id = a.post_id) as b where b.user_id = users.id and b.user_id &lt;&gt; ? order by b.created_at desc limit 50'</span>,[ auth.user.id, auth.user.id ])</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = NotificationController</span><br></pre></td></tr></table></figure><p>使用 <code>Database.raw</code> 来运行原生的 <code>sql</code>，以上这条 <code>sql</code> 是用来查询所有用户给自己所有文章点赞的用户信息和文章信息用于消息通知。</p><p>再来看看，<code>LUCID</code> 的模式是如何操作数据的：</p><p>使用 <code>LUCID</code> 模式，我们先需要用命令行工具创建 <code>Models</code>，如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adonis make:model User</span><br></pre></td></tr></table></figure><p>自动生成代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Model = use(<span class="string">'Model'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">extends</span> <span class="title">Model</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = User</span><br></pre></td></tr></table></figure><p>模型和模型之间需要定义一些关系，如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">const</span> Model = use(<span class="string">'Model'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">extends</span> <span class="title">Model</span> </span>&#123;</span><br><span class="line">  profile () &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.hasOne(<span class="string">'App/Models/Profile'</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="built_in">module</span>.exports = User</span><br></pre></td></tr></table></figure><p>意思是 一个用户对应一个用户信息档案，<strong>一对一</strong> 的关系</p><p>定义好关系之后，就可以方便的获取数据，如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> User = use(<span class="string">'App/Models/User'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> user = <span class="keyword">await</span> User.find(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">const</span> userProfile = <span class="keyword">await</span> user.profile().fetch()</span><br></pre></td></tr></table></figure><p>意思是，从用户表和用户个人信息表里获取用户 <code>id</code> 是 <code>1</code> 的用户信息及个人信息，</p><p>其中，关系可以定义为 <strong>3</strong> 种 <strong>一对一、一对多、多对多</strong> ，多对多需要定义中间表</p><p>再来看看，上面的应用中的实际应用，如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">async</span> update (&#123; params, request, response, session, auth &#125;) &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; title, content, user_id, tags &#125; = request.all()</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> post = <span class="keyword">await</span> Post.findOrFail(params.id)</span><br><span class="line">  post.merge(&#123; title, content&#125;)</span><br><span class="line">  <span class="keyword">await</span> post.save()</span><br><span class="line"></span><br><span class="line">  <span class="keyword">await</span> post.tags().sync(tags)</span><br><span class="line"></span><br><span class="line">  session.flash(&#123;</span><br><span class="line">    type: <span class="string">'primary'</span>,</span><br><span class="line">    message: <span class="string">'Post updated successfully.'</span></span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> response.redirect(</span><br><span class="line">    Route.url(<span class="string">'PostController.show'</span>, &#123;</span><br><span class="line">      id: post.id</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>以上，是更新文章的方法，<strong>文章</strong> 和 <strong>标签</strong> 是 <strong>多对多</strong> 的关系，一个标签可以属于多篇文章，一篇文章可以有多个标签，<code>await post.tags().sync(tags)</code> 这句代码就可以通过 <code>Models</code> 里定义的关系自动把标签和文章关联起来保存到 <code>posts</code> 和 <code>tags</code> 表里且把关联关系保存到中间表 <code>post_tag</code>。</p><p>当然，<code>Adonisjs</code> 提供了很多方便的方法，想了解更多的话需要您花点时间去了解学习。</p><h4 id="视图"><a href="#视图" class="headerlink" title="视图"></a>视图</h4><p><code>Adonisjs</code> 框架里视图使用了 <code>edge</code> 模板，我们可以使用命令行工具创建视图文件，如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adonis make:view post</span><br></pre></td></tr></table></figure><p>我看可以看下简单的例子：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@loggedIn</span><br><span class="line">  <span class="tag">&lt;<span class="name">h2</span>&gt;</span> You are logged in <span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">@else</span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span> <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"/login"</span>&gt;</span>Click here<span class="tag">&lt;/<span class="name">a</span>&gt;</span> to login <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">@endloggedIn</span><br></pre></td></tr></table></figure><p>视图模板里可以使用标签来做逻辑判断，视图模板就没什么好说的，基本都是通用的，关于 <code>edge</code> 视图模板更多语法 <a href="https://edge.adonisjs.com/docs/syntax-guide" target="_blank" rel="noopener">Edge官方文档</a></p><p>最后，<code>Adonisjs</code> 框架还提供了很多其它的实用工具，如：<code>Middleware</code> 中间件、<code>Validator</code> 验证器、<code>Error Handling</code> 自定义异常、<code>Events</code> 事件、<code>Mails</code> 邮件、<code>Websocket</code> 等来处理各种问题。</p><h2 id="Node-js项目发布到阿里云服务器"><a href="#Node-js项目发布到阿里云服务器" class="headerlink" title="Node.js项目发布到阿里云服务器"></a>Node.js项目发布到阿里云服务器</h2><p>首先，我们需要用 <code>ssh</code> 连接到阿里云（或者其他服务器供应商）的主机上，安装一些必要的工具。</p><h3 id="工具安装"><a href="#工具安装" class="headerlink" title="工具安装"></a>工具安装</h3><h4 id="安装-epel-release-软件包仓库"><a href="#安装-epel-release-软件包仓库" class="headerlink" title="安装 epel-release 软件包仓库"></a>安装 epel-release 软件包仓库</h4><p>我们需要安装 <code>epel-release</code> 软件包仓库，<code>epel-release</code> 里面有很多最新的软件包，如，之后安装的 <code>git</code> 就会用到</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo yum install epel-release - y</span><br></pre></td></tr></table></figure><h4 id="安装-Git-版本控制命令行工具"><a href="#安装-Git-版本控制命令行工具" class="headerlink" title="安装 Git 版本控制命令行工具"></a>安装 Git 版本控制命令行工具</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo yum install git -y</span><br></pre></td></tr></table></figure><h3 id="准备-Node-js-运行环境"><a href="#准备-Node-js-运行环境" class="headerlink" title="准备 Node.js 运行环境"></a>准备 Node.js 运行环境</h3><p>接下来，我们需要安装 <code>Node.js</code> 以便我们的 <code>Node.js</code> 项目能够跑起来，我们可以使用 <code>nvm</code> 安装和管理 <code>Node.js</code> ，使用 <code>nrm</code> 来管理切换安装源。</p><h4 id="安装-nvm"><a href="#安装-nvm" class="headerlink" title="安装 nvm"></a>安装 nvm</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash</span><br></pre></td></tr></table></figure><p>安装好之后,我们需要配置下环境变量，以便能够在命令行使用 <code>nvm</code> 命令，用 <code>vi ~/.bash_profile</code> 编辑下配置文件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vi ~/.bash_profile</span><br></pre></td></tr></table></figure><p>加入以下代码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> NVM_DIR=<span class="string">"<span class="variable">$HOME</span>/.nvm"</span></span><br><span class="line">[ -s <span class="string">"<span class="variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="string">"<span class="variable">$NVM_DIR</span>/nvm.sh"</span></span><br><span class="line">[ -s <span class="string">"<span class="variable">$NVM_DIR</span>/bash_completion"</span> ] &amp;&amp; \. <span class="string">"<span class="variable">$NVM_DIR</span>/bash_completion"</span></span><br></pre></td></tr></table></figure><p>然后，在 <code>source ~/.bash_profile</code> 刷新下配置文件，让它生效</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.bash_profile</span><br></pre></td></tr></table></figure><p>此时，我们就可以使用 <code>nvm</code> 来安装 <code>Node.js</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm install node</span><br></pre></td></tr></table></figure><p>安装好后，可以使用 <code>nvm list</code> 来查看有哪些版本可以使用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm list</span><br></pre></td></tr></table></figure><p>结果：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">-&gt;     v10.13.0</span><br><span class="line">        v11.2.0</span><br><span class="line">         system</span><br><span class="line">default -&gt; v10.13.0</span><br><span class="line">node -&gt; stable (-&gt; v11.2.0) (default)</span><br><span class="line">stable -&gt; 11.2 (-&gt; v11.2.0) (default)</span><br><span class="line">iojs -&gt; N/A (default)</span><br><span class="line">lts/* -&gt; lts/dubnium (-&gt; v10.13.0)</span><br><span class="line">lts/argon -&gt; v4.9.1 (-&gt; N/A)</span><br><span class="line">lts/boron -&gt; v6.14.4 (-&gt; N/A)</span><br><span class="line">lts/carbon -&gt; v8.13.0 (-&gt; N/A)</span><br><span class="line">lts/dubnium -&gt; v10.13.0</span><br></pre></td></tr></table></figure><p>我使用的是 <em>v10.13.0</em> 的版本，默认安装的都是比较新的版本，可能是 <em>v11.2.0</em> 或 <em>v11.1.0</em>，所以我们也可以用 <code>nvm install v10.13.0</code> 来安装指定版本。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm install v10.13.0</span><br></pre></td></tr></table></figure><p>然后，就可以使用 <code>nvm use  v10.13.0</code> 来使用指定版本</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm use nvm v10.13.0</span><br></pre></td></tr></table></figure><p>结果：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Now using node v10.13.0 (npm v6.4.1)</span><br></pre></td></tr></table></figure><h4 id="安装-nrm-管理安装源"><a href="#安装-nrm-管理安装源" class="headerlink" title="安装 nrm 管理安装源"></a>安装 nrm 管理安装源</h4><p>使用 npm 安装的程序包，默认的来源是 <a href="http://registry.npmjs.org" target="_blank" rel="noopener">http://registry.npmjs.org</a>，国内的下载速度会有些慢，我们可以是 <code>nrm</code> 来切换到 <code>taobao</code> 的源</p><p><strong>安装 nrm</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install nrm --global</span><br></pre></td></tr></table></figure><p><strong>切换到 taobao 源</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nrm use taobao</span><br></pre></td></tr></table></figure><h3 id="准备项目"><a href="#准备项目" class="headerlink" title="准备项目"></a>准备项目</h3><p>以上工作完成之后，我们的服务器就可以正常运行 <code>Node.js</code> 项目，现在我们需要把本地的项目上传到服务器，上传方法有很多，如：</p><ul><li>可以使用 <code>git</code>，先把项目传到 <strong>GitHub</strong>，然后用 <code>git</code> 下载到服务器</li><li>可以是 <strong>FTP</strong> 工具</li><li>可以是命令上传 <code>scp -r 本地目录  root@服务器IP:/var/www/</code></li></ul><p>发项目文件上传到服务器的指定目录下，如：<code>www</code></p><p>接下来，我们可以是 <strong>PM2</strong> 来管理 <strong>Node</strong> 进程，先需要安装 <strong>PM2</strong></p><h4 id="安装PM2"><a href="#安装PM2" class="headerlink" title="安装PM2"></a>安装PM2</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install pm2@latest --global</span><br></pre></td></tr></table></figure><p>这些工作作为之后，就可以来测试一下，启动项目，在本地访问服务器 <code>IP:PORT</code> 来测试是否可以访问</p><h4 id="测试项目是否可以运行"><a href="#测试项目是否可以运行" class="headerlink" title="测试项目是否可以运行"></a>测试项目是否可以运行</h4><p>在测试之前，我们需要改下应用的配置文件，<code>adonisjs</code> 框架里是 <code>.env</code> 文件，修改下 <code>HOST</code> 的值：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">HOST=0.0.0.0</span><br><span class="line">PORT=3333</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p><code>HOST</code> 默认是 <em>127.0.0.1</em>，需要改成 <em>0.0.0.0</em> 这样就可以在自己电脑上用服务器 <code>IP:PORT</code> 来访问应用</p><p>改完后，进入到项目的根目录，运行应用，<code>adonisjs</code> 的启动文件是 <code>server.js</code>，如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pm2 start server.js</span><br></pre></td></tr></table></figure><p>如启动成功会提示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[PM2] Applying action restartProcessId on app [server](ids: 0)</span><br><span class="line">[PM2] [server](0) ✓</span><br><span class="line">[PM2] Process successfully started</span><br><span class="line">┌──────────┬────┬─────────┬──────┬──────┬────────┬─────────┬────────┬─────┬──────────┬──────┬──────────┐</span><br><span class="line">│ App name │ id │ version │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem      │ user │ watching │</span><br><span class="line">├──────────┼────┼─────────┼──────┼──────┼────────┼─────────┼────────┼─────┼──────────┼──────┼──────────┤</span><br><span class="line">│ server   │ 0  │ 4.1.0   │ fork │ 7171 │ online │ 30      │ 0s     │ 0%  │ 3.4 MB   │ root │ disabled │</span><br><span class="line">└──────────┴────┴─────────┴──────┴──────┴────────┴─────────┴────────┴─────┴──────────┴──────┴──────────┘</span><br><span class="line"> Use `pm2 show &lt;id|name&gt;` to get more details about an app</span><br></pre></td></tr></table></figure><p>然后，在自己电脑上用服务器 <code>IP:PORT</code> 来访问应用。</p><h3 id="Nginx-代理"><a href="#Nginx-代理" class="headerlink" title="Nginx 代理"></a>Nginx 代理</h3><p>为了让服务器更好地处理网络请求，我们需要添加使用 <strong>Nginx 反向代理</strong> 把请求转发给 <code>Node.js</code> 应用</p><h4 id="安装-Nginx"><a href="#安装-Nginx" class="headerlink" title="安装 Nginx"></a>安装 Nginx</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo yum install nginx -y</span><br></pre></td></tr></table></figure><p>如果你的服务之前安装过可不用安装，我的阿里云服务器运行了 4 个站点之前安装过，之后我只需添加配置就行。</p><h4 id="启动-Nginx"><a href="#启动-Nginx" class="headerlink" title="启动 Nginx"></a>启动 Nginx</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl start nginx</span><br></pre></td></tr></table></figure><h4 id="配置-Nginx"><a href="#配置-Nginx" class="headerlink" title="配置 Nginx"></a>配置 Nginx</h4><p>一般情况 <strong>Nginx</strong> 安装好后会有 <em>/etc/nginx/conf.d</em>  目录，进入这个目录，创建一个配置文件为 <code>Node.js</code> 而准备，名字可随意命名，如：<code>adonis.conf</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">  listen 80;</span><br><span class="line">  location / &#123;</span><br><span class="line">      proxy_pass http://127.0.0.1:3333;</span><br><span class="line">      proxy_http_version 1.1;</span><br><span class="line">      proxy_set_header Upgrade <span class="variable">$http_upgrade</span>;</span><br><span class="line">      proxy_set_header Connection <span class="string">'upgrade'</span>;</span><br><span class="line">      proxy_set_header Host <span class="variable">$host</span>;</span><br><span class="line">      proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">      proxy_set_header X-Forwarded-Proto <span class="variable">$scheme</span>;</span><br><span class="line">      proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">      proxy_cache_bypass <span class="variable">$http_upgrade</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，在 <strong>Nginx</strong> 的主配置文件里把刚才新创建的配置文件（<em>/etc/nginx/nginx.conf</em>） <code>include</code> 进去就可以,如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">include /etc/nginx/conf.d/*.conf;</span><br></pre></td></tr></table></figure><p>因为，我的主机里运行了4个站点，<code>*</code> 的意思就是加载这个目录下的所有配置文件</p><p>然后，记得把刚才项目里的 <em>.env</em> 配置文件改成 <em>127.0.0.1</em> ，因为我们现在使用了代理，网络请求交给了 <strong>Nginx</strong></p><p>再进入到项目的根目录下运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pm2 stop server.js <span class="comment">#停止项目</span></span><br><span class="line">pm2 start server.js <span class="comment">#启动项目</span></span><br></pre></td></tr></table></figure><p>这时候再用服务器 <strong>IP</strong> 访问就是用的 <strong>Nginx</strong> 去处理请求</p><h4 id="域名和SSL"><a href="#域名和SSL" class="headerlink" title="域名和SSL"></a>域名和SSL</h4><p>如果你有域名可以去对应的供应商解析好，如想使用 <code>https</code> 协议，也可以去对应的供应商下载好证书（下载好的证书要放到服务器某个目录里）。</p><p>再修改下刚才创建的配置文件，让它能够支持 <code>https</code> 和 域名 访问：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">  listen 80;</span><br><span class="line">      listen 443 ssl http2; <span class="comment">#SSL</span></span><br><span class="line">  server_name a.lishaoy.net; <span class="comment">#域名</span></span><br><span class="line">  ssl on;</span><br><span class="line"></span><br><span class="line">  ssl_certificate /etc/letsencrypt/live/a.lishaoy.net/server.pem; <span class="comment">#证书目录</span></span><br><span class="line">  ssl_certificate_key /etc/letsencrypt/live/a.lishaoy.net/server.key; <span class="comment">#证书目录</span></span><br><span class="line">  ssl_protocols TLSv1.1 TLSv1.2;</span><br><span class="line">  ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;</span><br><span class="line">  ssl_prefer_server_ciphers on;</span><br><span class="line">  ssl_session_cache shared:SSL:10m;</span><br><span class="line">  ssl_session_timeout 10m;</span><br><span class="line">  <span class="keyword">if</span> (<span class="variable">$ssl_protocol</span> = <span class="string">""</span>) &#123;</span><br><span class="line">    rewrite ^(.*) https://<span class="variable">$host</span><span class="variable">$1</span> permanent;</span><br><span class="line">  &#125;</span><br><span class="line">  error_page 497  https://<span class="variable">$host</span><span class="variable">$request_uri</span>;</span><br><span class="line"></span><br><span class="line">  error_page 404 /404.html;</span><br><span class="line">  error_page 502 /502.html;</span><br><span class="line"></span><br><span class="line">  location / &#123;</span><br><span class="line">      proxy_pass http://localhost:3333;</span><br><span class="line">      proxy_http_version 1.1;</span><br><span class="line">      proxy_set_header Upgrade <span class="variable">$http_upgrade</span>;</span><br><span class="line">      proxy_set_header Connection <span class="string">'upgrade'</span>;</span><br><span class="line">      proxy_set_header Host <span class="variable">$host</span>;</span><br><span class="line">      proxy_set_header X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line">      proxy_set_header X-Forwarded-Proto <span class="variable">$scheme</span>;</span><br><span class="line">      proxy_set_header X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line">      proxy_cache_bypass <span class="variable">$http_upgrade</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样再重启 <code>Ningx</code> 服务和项目的服务，就大功告成了。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/adonisjs.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Flutter：手拉手带你极速构建漂亮的跨平台移动应用 ✿ 初体验（实战）</title>
      <link>https://h.lishaoy.net/fristFlutter.html</link>
      <guid>https://h.lishaoy.net/fristFlutter.html</guid>
      <pubDate>Sat, 10 Nov 2018 17:04:15 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/fristFlutter/flutterCover7.png&quot; class=&quot;full-image&quot; alt=&quot;Flutter&quot; title=&quot;Flutter&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;我们上篇文章已经把 &lt;strong&gt;Flutter&lt;/strong&gt; 的开发运行环境搭建好了 &lt;a href=&quot;https://h.lishaoy.net/flutterInstall.html&quot;&gt;Flutter：环境搭建&lt;/a&gt; ，本篇文章将完成您的第一个 &lt;strong&gt;Flutter&lt;/strong&gt; 小应用，不用但是不会，跟着文章做就行，开始不必太纠结这些代码细节，明白它是干什么的就行，只是一个使用体验，体验 &lt;strong&gt;Flutter&lt;/strong&gt; 框架给你带来的开发感受，后面会有具体的组件（widget）的文章。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/fristFlutter/flutterCover7.png" class="full-image" alt="Flutter" title="Flutter"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>我们上篇文章已经把 <strong>Flutter</strong> 的开发运行环境搭建好了 <a href="https://h.lishaoy.net/flutterInstall.html">Flutter：环境搭建</a> ，本篇文章将完成您的第一个 <strong>Flutter</strong> 小应用，不用但是不会，跟着文章做就行，开始不必太纠结这些代码细节，明白它是干什么的就行，只是一个使用体验，体验 <strong>Flutter</strong> 框架给你带来的开发感受，后面会有具体的组件（widget）的文章。</p><hr><a id="more"></a><h2 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h2><p>在上篇文章我们已经创建了一个 Flutter 项目 <span class="label success">new_flutter</span>，如果您还没有创建，可以在终端执行以下命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/desktop <span class="comment">#进入桌面</span></span><br><span class="line">flutter create new_flutter <span class="comment">#创建 Flutter 项目</span></span><br><span class="line"><span class="built_in">cd</span> new_flutter <span class="comment">#进入项目</span></span><br><span class="line">code ./ <span class="comment">#用 VS Code 打开</span></span><br></pre></td></tr></table></figure><p>用 VS Code 打开项目后，按 <strong>F5</strong> 选择模拟器运行项目看看效果，这些操作在上篇文章都已经做过了，您应该有些印象，如成功的话，会看到如图效果：</p><p><img src="https://cdn.lishaoy.net/flutterInstall/demo.png" alt="no-shadow" title="Flutter run"></p><p>上图界面是 <code>flutter create</code> 命令创建项目时，给我们的案例（计数器），这些代码在 <em><span class="label default"> lib->main.dart</span></em> 文件里，如你关注的话可以看看这些代码都做了些什么，不过现在我们不需要这些代码，<kbd>⌘</kbd> - <kbd>A</kbd> 全选删除，我们需要自己写，如下：</p><figure class="highlight dart"><figcaption><span>Dart</span><a href="https://h.lishaoy.net/fristFlutter.html#创建项目">main.dart</a></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="keyword">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> main() &#123;</span><br><span class="line">  runApp(</span><br><span class="line">    Center(</span><br><span class="line">      child: Text(</span><br><span class="line">        <span class="string">'Hello'</span>,</span><br><span class="line">        textDirection: TextDirection.ltr,</span><br><span class="line">        style: TextStyle(fontSize: <span class="number">36.0</span>),</span><br><span class="line">      ),</span><br><span class="line">    )</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>写入以上代码，按 <kbd>⌘</kbd> - <kbd>⇧</kbd> - <kbd>P</kbd> 打开 VS Code 的命令面板，搜索 <code>hot restart</code> 执行命令，效果如图：</p><div style="padding: 0 30%;"><img src="https://cdn.lishaoy.net/fristFlutter/flutter_hello.png" alt="no-shadow" title="hello"></div><p><code>main()</code> 是 Flutter 的入口函数，我们一般不会把代码写在里面，我们来修改下代码（自定义 widget）：</p><figure class="highlight dart"><figcaption><span>Dart</span><a href="https://h.lishaoy.net/fristFlutter.html#创建项目">main.dart</a></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="keyword">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> main() =&gt; runApp(App());</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Center(</span><br><span class="line">      child: Text(</span><br><span class="line">        <span class="string">'Hello'</span>,</span><br><span class="line">        textDirection: TextDirection.ltr,</span><br><span class="line">        style: TextStyle(fontSize: <span class="number">36.0</span>),</span><br><span class="line">      ),</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>运行效果是一样的，这里我们自定义了一个 <code>StatelessWidget</code> 类型的 widget 叫 <span class="label primary">App</span>。</p><h2 id="新增-Material-AppBar"><a href="#新增-Material-AppBar" class="headerlink" title="新增 Material AppBar"></a>新增 Material AppBar</h2><p>我们再来修改下代码，让我们的应用去使用 <span class="label success">Material Design</span> 的 AppBar</p><figure class="highlight dart"><figcaption><span>Dart</span><a href="https://h.lishaoy.net/fristFlutter.html#新增-Material-AppBar">main.dart</a></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">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> main() =&gt; runApp(App());</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> MaterialApp(</span><br><span class="line">      home: Scaffold(</span><br><span class="line">        appBar: AppBar(</span><br><span class="line">          title: Text(</span><br><span class="line">            <span class="string">'lishaoy.net'</span>.toUpperCase(),</span><br><span class="line">            style: TextStyle(letterSpacing: <span class="number">3.0</span>),</span><br><span class="line">          ),</span><br><span class="line">        ),</span><br><span class="line">        body: Hello(),</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Hello</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Center(</span><br><span class="line">      child: Text(</span><br><span class="line">        <span class="string">'Hello'</span>,</span><br><span class="line">        textDirection: TextDirection.ltr,</span><br><span class="line">        style: TextStyle(fontSize: <span class="number">36.0</span>),</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行效果如图：</p><div style="padding: 0 30%;"><img src="https://cdn.lishaoy.net/fristFlutter/Appbar.png" alt="no-shadow" title="hello"></div><div class="note success"><p><i class="fa fa-fw fa-bell faa-horizontal animated faa-slow" style="color: #009688;"></i><strong>Tip：</strong></p><ol><li>自定义了一个 Hello 的 <span class="label info">StatelessWidget</span> 类型的 widget</li><li>新增了 MaterialApp ，这里面就有我们需要用的 AppBar （MaterialApp 里面有很多 <span class="label info">Material Design</span> 风格的组件，后面的文章提到）</li><li>Scaffold 可以理解为 MaterialApp 布局的架子，<span class="label info">Material Design</span> 风格的组件都会放到这里面</li><li>AppBar 就是顶部蓝色这部分，加了一个 title 转为大写和增加词间距</li><li>body 是主显示区，下面白色部分，放的是我们自定义的 Hello 小部件</li></ol></div>  <h2 id="新增列表视图（ListView）"><a href="#新增列表视图（ListView）" class="headerlink" title="新增列表视图（ListView）"></a>新增列表视图（ListView）</h2><p>在新增列表视图（ListView）之前，我们需要准备一些数据，我们可以在 lib 目录下新建一个目录 model 和文件 <span class="label default">post.dart</span>，如图：</p><p><img src="https://cdn.lishaoy.net/fristFlutter/model.png" alt="post.dart" width="26%" title="post.dart" align="center"></p><p>然后，把在 <span class="label success">GitHub</span> 准备好的 <a href="https://github.com/persilee/flutter_pro/blob/master/lib/model/post.dart" target="_blank" rel="noopener">post.dart</a> 数据放到里面。</p><p>现在我们再来改进下代码，让它更具有维护性，我们把 <code>home</code> 属性下的 Scaffold 放到单独的 widget ，取名为 <span class="label default">HomePage</span></p><figure class="highlight dart"><figcaption><span>Dart</span><a href="https://h.lishaoy.net/fristFlutter.html#新增列表视图（ListView）">main.dart</a></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="keyword">import</span> <span class="string">'package:flutter/material.dart'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> main() =&gt; runApp(App());</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> MaterialApp(</span><br><span class="line">      home: HomePage(),</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="class"><span class="keyword">class</span> <span class="title">HomePage</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">        appBar: AppBar(</span><br><span class="line">          title: Text(</span><br><span class="line">            <span class="string">'lishaoy.net'</span>.toUpperCase(),</span><br><span class="line">            style: TextStyle(letterSpacing: <span class="number">3.0</span>),</span><br><span class="line">          ),</span><br><span class="line">        ),</span><br><span class="line">        body: Hello(),</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>接下来，我们来创建 <span class="label primary">视图列表</span> </p><p>首先，在头部引入刚才创建的数据 <code>post.dart</code> 文件</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'./model/post.dart'</span>;</span><br></pre></td></tr></table></figure><p>然后，再把 <code>body</code> 下的 Hello() 换成 <code>ListView</code>，如下：</p><figure class="highlight dart"><figcaption><span>Dart</span><a href="https://h.lishaoy.net/fristFlutter.html#新增列表视图（ListView）">main.dart</a></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="class"><span class="keyword">class</span> <span class="title">HomePage</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">        appBar: AppBar(</span><br><span class="line">          title: Text(</span><br><span class="line">            <span class="string">'lishaoy.net'</span>.toUpperCase(),</span><br><span class="line">            style: TextStyle(letterSpacing: <span class="number">3.0</span>),</span><br><span class="line">          ),</span><br><span class="line">        ),</span><br><span class="line">        body: ListView.builder(</span><br><span class="line">          itemCount: posts.length,</span><br><span class="line">          itemBuilder: (BuildContext context, <span class="built_in">int</span> index) =&gt; Text(posts[index].title),</span><br><span class="line">        ),</span><br><span class="line">      );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>效果如图：</p><div style="padding: 0 30%;"><img src="https://cdn.lishaoy.net/fristFlutter/listView.png" alt="no-shadow" title="ListView"></div><h2 id="完善列表项目"><a href="#完善列表项目" class="headerlink" title="完善列表项目"></a>完善列表项目</h2><p>现在，数据已经呈现在应用界面上了，接下来要做的是让数据展示更友好一点。</p><p>我们再来改造一下 <code>itemBuilder</code> 下的方法：</p><figure class="highlight dart"><figcaption><span>Dart</span><a href="https://h.lishaoy.net/fristFlutter.html#完善列表项目">main.dart</a></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="class"><span class="keyword">class</span> <span class="title">HomePage</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      backgroundColor: Colors.grey[<span class="number">100</span>],</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(</span><br><span class="line">          <span class="string">'lishaoy.net'</span>.toUpperCase(),</span><br><span class="line">          style: TextStyle(letterSpacing: <span class="number">3.0</span>),</span><br><span class="line">        ),</span><br><span class="line">      ),</span><br><span class="line">      body: ListView.builder(</span><br><span class="line">        itemCount: posts.length,</span><br><span class="line">        itemBuilder: (BuildContext context, <span class="built_in">int</span> index) =&gt; Container(</span><br><span class="line">              margin: EdgeInsets.all(<span class="number">8.0</span>),</span><br><span class="line">              color: Colors.white,</span><br><span class="line">              child: Column(</span><br><span class="line">                children: &lt;Widget&gt;[</span><br><span class="line">                  Image.network(</span><br><span class="line">                    posts[index].imageUrl,</span><br><span class="line">                    fit: BoxFit.cover,</span><br><span class="line">                  ),</span><br><span class="line">                  SizedBox(</span><br><span class="line">                    height: <span class="number">16.0</span>,</span><br><span class="line">                  ),</span><br><span class="line">                  Text(</span><br><span class="line">                    posts[index].title,</span><br><span class="line">                    style: Theme.of(context).textTheme.title,</span><br><span class="line">                  ),</span><br><span class="line">                  Text(</span><br><span class="line">                    posts[index].author,</span><br><span class="line">                    style: Theme.of(context).textTheme.subhead,</span><br><span class="line">                  ),</span><br><span class="line">                  SizedBox(</span><br><span class="line">                    height: <span class="number">16.0</span>,</span><br><span class="line">                  ),</span><br><span class="line">                ],</span><br><span class="line">              ),</span><br><span class="line">            ),</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行效果如图：</p><div style="padding: 0 30%;"><img src="https://cdn.lishaoy.net/fristFlutter/listView1.png" alt="no-shadow" title="ListView"></div><div class="note success"><p><i class="fa fa-fw fa-bell faa-horizontal animated faa-slow" style="color: #009688;"></i><strong>Tip：</strong></p><ol><li><span class="label info">itemBuilder</span> 属性是个方法，可以传 2 个参数，context（内容）、index（索引）</li><li>我们用了 <span class="label info">Container</span> 的 widget 来装载我们的项目，因为 <span class="label info">Container</span> 有很多属性，例如 margin、padding、color、width、height等，后面我们会慢慢的熟悉它</li><li>我们用了一个 <span class="label info">Column</span> 的 widget 来布局，因为我们的图片、标题、小标题要纵向排列（Row、Column等一些布局的 widget 我们会经常用到）</li><li>用了 <span class="label info">Image.network</span> 来加载一个来自网络的图片（Image.asset可以加载本地图像）</li><li><span class="label info">Theme.of(context).textTheme.title</span> 是用 <span class="label info">Material Design</span> 主题里的标题样式</li></ol></div>  <p>最后，为了可阅读性和维护性考量，我们再可以调整一下代码 <span class="label success">ListView.builder</span> 方法单独提炼出来：</p><figure class="highlight dart"><figcaption><span>Dart</span><a href="https://h.lishaoy.net/fristFlutter.html#完善列表项目">main.dart</a></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="class"><span class="keyword">class</span> <span class="title">HomePage</span> <span class="keyword">extends</span> <span class="title">StatelessWidget</span> </span>&#123;</span><br><span class="line">  Widget _listItemBuilder(BuildContext context, <span class="built_in">int</span> index) =&gt; Container(</span><br><span class="line">        margin: EdgeInsets.all(<span class="number">8.0</span>),</span><br><span class="line">        color: Colors.white,</span><br><span class="line">        child: Column(</span><br><span class="line">          children: &lt;Widget&gt;[</span><br><span class="line">            Image.network(</span><br><span class="line">              posts[index].imageUrl,</span><br><span class="line">              fit: BoxFit.cover,</span><br><span class="line">            ),</span><br><span class="line">            SizedBox(</span><br><span class="line">              height: <span class="number">16.0</span>,</span><br><span class="line">            ),</span><br><span class="line">            Text(</span><br><span class="line">              posts[index].title,</span><br><span class="line">              style: Theme.of(context).textTheme.title,</span><br><span class="line">            ),</span><br><span class="line">            Text(</span><br><span class="line">              posts[index].author,</span><br><span class="line">              style: Theme.of(context).textTheme.subhead,</span><br><span class="line">            ),</span><br><span class="line">            SizedBox(</span><br><span class="line">              height: <span class="number">16.0</span>,</span><br><span class="line">            ),</span><br><span class="line">          ],</span><br><span class="line">        ),</span><br><span class="line">      );</span><br><span class="line"></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  Widget build(BuildContext context) &#123;</span><br><span class="line">    <span class="keyword">return</span> Scaffold(</span><br><span class="line">      backgroundColor: Colors.grey[<span class="number">100</span>],</span><br><span class="line">      appBar: AppBar(</span><br><span class="line">        title: Text(</span><br><span class="line">          <span class="string">'lishaoy.net'</span>.toUpperCase(),</span><br><span class="line">          style: TextStyle(letterSpacing: <span class="number">3.0</span>),</span><br><span class="line">        ),</span><br><span class="line">      ),</span><br><span class="line">      body: ListView.builder(</span><br><span class="line">        itemCount: posts.length,</span><br><span class="line">        itemBuilder: _listItemBuilder,</span><br><span class="line">      ),</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>现在，第一个漂亮的界面已经完成，后面的文章我们一起来完成其他部分。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/fristFlutter.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Flutter：手拉手带你极速构建漂亮的跨平台移动应用 ✿ 环境搭建</title>
      <link>https://h.lishaoy.net/flutterInstall.html</link>
      <guid>https://h.lishaoy.net/flutterInstall.html</guid>
      <pubDate>Thu, 08 Nov 2018 02:22:29 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/flutterInstall/flutterCover8.png&quot; class=&quot;full-image&quot; alt=&quot;Flutter&quot; title=&quot;Flutter&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;上篇文章带大家认识了 &lt;code&gt;Flutter&lt;/code&gt; ，想必大家已迫不及待的想练练手，所以要行动起来，现在这篇文章就带您搭建一个 &lt;code&gt;Flutter&lt;/code&gt; 运行及开发环境。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/flutterInstall/flutterCover8.png" class="full-image" alt="Flutter" title="Flutter"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>上篇文章带大家认识了 <code>Flutter</code> ，想必大家已迫不及待的想练练手，所以要行动起来，现在这篇文章就带您搭建一个 <code>Flutter</code> 运行及开发环境。</p><hr><a id="more"></a><h2 id="安装-Flutter-SDK"><a href="#安装-Flutter-SDK" class="headerlink" title="安装 Flutter SDK"></a>安装 Flutter SDK</h2><p>想要在本地电脑上运行 <strong>Flutter</strong> ，需要安装 <strong>Flutter SDK</strong> 才可以运行， <strong>SDK</strong> 里面有一些用于创建、构建、测试和编译应用程序的命令行工具等，这些在开发的时候会用到。</p><p>首先，我们有 2 种方法获取 <strong>SDK</strong></p><ul><li>可以到 <a href="https://flutter.io/docs/development/tools/sdk/archive#macos" target="_blank" rel="noopener">下载 Flutter SDK </a> 到本地电脑</li><li>可以用 <code>git clone</code> 命令下载到本地电脑</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> -b master https://github.com/flutter/flutter.git</span><br></pre></td></tr></table></figure><p>其次，把下载下来的 <strong>Flutter SDK</strong> 解压，放到系统的某个目录，比如我是放到： <code>/Applications/flutter</code> ，如图：</p><p><img src="https://cdn.lishaoy.net/flutterInstall/flutterSDK.png" alt="no-shadow" title="Flutter SDK"></p><h2 id="配置环境变量"><a href="#配置环境变量" class="headerlink" title="配置环境变量"></a>配置环境变量</h2><p>配置环境变量的目的是为了让 <strong>Flutter SDK</strong> 命令行工具在全局范围都起作用，以便开发使用。</p><p>首先，您可以用编辑器打开主目录下的 <code>.bash_profile</code>，或者用 <code>vi</code> 命令编辑，我习惯用 <code>vi</code> 命令，如下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vi <span class="variable">$HOME</span>/.bash_profile</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></pre></td><td class="code"><pre><span class="line">export PATH=$PATH:/Applications/flutter/bin</span><br><span class="line">export PUB_HOSTED_URL=https://pub.flutter-io.cn</span><br><span class="line">export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn</span><br></pre></td></tr></table></figure><div class="note warning"><p><i class="fa fa-fw fa-bell faa-horizontal animated faa-slow" style="color: #faab33;"></i> <strong>Tips：</strong> <br>第一行 <span class="label danger">export PATH=$PATH:/Applications/flutter/bin</span> 中的 <span class="label danger">/Applications/flutter/bin</span> 就是刚才下载的 <strong>Flutter SDK</strong> 解压后放在本地电脑的目录，您要根据自己操作更改为自己电脑对应的目录。<br>第二、三行为解决国内下载或更新资源慢的国内镜像，配置这个下载或更新资源会快一些。</p></div>  <p>再执行 <code>source $HOME/.bash_profile</code> 命令刷新当前命令行窗口，或者关掉当前命令行窗口重新打开，效果一样</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> <span class="variable">$HOME</span>/.bash_profile</span><br></pre></td></tr></table></figure><p>再执行 <code>flutter --help</code>，来测试环境变量是否配置成功，如图：</p><p><img src="https://cdn.lishaoy.net/flutterSDK3.png" alt="no-shadow" title="flutter help"></p><div class="note warning"><p><i class="fa fa-fw fa-bell  faa-horizontal animated faa-slow" style="color: #faab33;"></i> <strong>Tips：</strong> 如果你使用的是 <span class="label danger">zsh</span>，需要在 ~/.zshrc 文件中添加：<span class="label danger">source ~/.bash_profile</span> ，否则 <span class="label danger">flutter</span> 命令将无法运行。</p></div> <h2 id="配置-iOS-开发环境"><a href="#配置-iOS-开发环境" class="headerlink" title="配置 iOS 开发环境"></a>配置 iOS 开发环境</h2><p>想用 <strong>Flutter</strong> 为 iOS 平台开发应用，需要安装 Xcode，我们可以去苹果应用商店下载。</p><p>安装好 Xcode 后，你需要打开一次 Xcode 同意许可协议（会提示），或者执行 <code>sudo xcodebuild -license</code> 同意许可协议。</p><p>然后执行 <code>open -a Simulator</code> 命令，就可以打开一个模拟器，来运行和测试 <strong>Flutter</strong> 程序，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/Simulator2.png" alt="no-shadow" title="Simulator"></p><h2 id="配置-Android-开发环境"><a href="#配置-Android-开发环境" class="headerlink" title="配置 Android 开发环境"></a>配置 Android 开发环境</h2><p>想用 <strong>Flutter</strong> 为 Android 平台开发应用，需要下载安装 <a href="https://developer.android.com/studio/" target="_blank" rel="noopener">Android Studio</a>。</p><p>安装好 Android Studio 后，启动它，首次启动会安装最新的 <strong>Android SDK</strong> ，但是你可能会遇到这样的问题，如图：</p><p><img src="https://cdn.lishaoy.net/flutterInstall/AndroidStudio.png" alt="Android Studio" width="50%" title="Android Studio" align="center"></p><p>如果遇到这个问题应该就是网络问题（需要科学上网），点 <strong>Setup Proxy</strong> 来设置代理，如图：</p><p><img src="https://cdn.lishaoy.net/flutterInstall/AndroidStudio1.png" alt="Android Studio" width="50%" title="Android Studio" align="center"></p><p>如一切正常，就会提示你需要下载一些东西，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/AndroidStudio2.png" alt="Android Studio" width="85%" title="Android Studio" align="center"></p><p>点击 Finish 按钮后就会下载安装以上列表的东西,下载安装完 SDK 后，如图：</p><p><img src="https://cdn.lishaoy.net/flutterInstall/AndroidStudio3.png" alt="Android Studio" width="85%" title="Android Studio" align="center"></p><p>需要我们打开一个项目，我们可以用刚才已经配置好的 <strong>Flutter SDK</strong> 的命令行创建一个 Flutter 项目，如执行以下命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/desktop</span><br><span class="line">flutter create new_flutter</span><br></pre></td></tr></table></figure><p>命令执行完成后，在桌面就会生成一个 Flutter 项目，再用 Android Studio 打开，项目打开后会提示安装 Flutter 插件和依赖 Dart 语言插件 ，安装完之后我们可以去创建一个模拟器。</p><p>打开 <strong>Tools&gt;AVD Manager</strong> ，点击 <code>Create Virtual Device...</code> 来创建一个模拟器，选择一个设备，点击 Next，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/AndroidStudio4.png" alt="Android Studio" width="85%" title="Android Studio" align="center"></p><p>为模拟器选择一个系统镜像（我选择的是第一个），点击 Download ，下载完成后，点击 Next 后，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/AndroidStudio5.png" alt="Android Studio" width="85%" title="Android Studio" align="center"></p><p>最后，在模拟性能这里选择 <strong>Hardware - GLES 2.0</strong> 启动硬件加速，点击 Finish 完成</p><p><img src="https://cdn.lishaoy.net/flutterInstall/AndroidStudio6.png" alt="Android Studio" width="85%" title="Android Studio" align="center"></p><h2 id="配置编辑器"><a href="#配置编辑器" class="headerlink" title="配置编辑器"></a>配置编辑器</h2><p>前面我们已经配置好了 <strong>Flutter SDK</strong> 、<strong>iOS 模拟器</strong> 、<strong>Android 模拟器</strong> ，最后我们还需要配置一下编辑器，当然您可以选择 <code>Android Studio</code> 或者 <code>VS Code</code>，这里我选择的是轻量级的 <code>VS Code</code>。</p><div class="note default"><p>如对 VS Code 不是很熟悉，可参考我之前写的 <a href="https://h.lishaoy.net/VSCodeCodingSkills.html">VS Code 编辑技巧</a> </p></div>  <p>打开终端进入我们刚才新建的 Flutter 项目</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> new_flutter</span><br></pre></td></tr></table></figure><p>再用 VS Code 打开项目</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">code ./</span><br></pre></td></tr></table></figure><p>打开项目之后 <kbd>⌘</kbd> - <kbd>⇧</kbd> - <kbd>X</kbd> ,打开扩展，安装 <strong>Flutter</strong> 插件，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/flutter_install.gif" alt="Android Studio" width="88%" title="Flutter install" align="center"></p><p>完成之后，打开项目目录 <code>lib-&gt;main.dart</code> 文件， VS Code 会自动提示你安装 Dart 语言扩展包。</p><h2 id="运行项目"><a href="#运行项目" class="headerlink" title="运行项目"></a>运行项目</h2><p>现在，所有的准备工作都完成了，就可以开发、测试或运行项目了，在上面我们用 <code>Flutter create</code> 命令创建的 Flutter 项目，自带一个计数器的小功能，我们可以运行看看效果</p><p>首先，您需要执行 <code>flutter doctor</code> 来检查一下环境是否正常</p><p><img src="https://cdn.lishaoy.net/flutterInstall/flutter_run.png" alt="no-shadow" title="Flutter run"></p><p>如上图第二项提示 <code>Android license status unknown.</code> 意思是 Android 协议没安装好，可以执行以下命令，来解决问题</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flutter doctor --android-licenses</span><br></pre></td></tr></table></figure><p>如上图第三项是 iOS 真机的检查项，可以按照提示操作<br>如上图第四项是 Java 的编辑器检查，可不用理会，如你没有安装 IDEA 也不会有这个提示</p><p>其实在我另一台电脑上全部都配置好了 😝 ，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/flutter_run1.png" alt="no-shadow" title="Flutter run"></p><p>最后，在 VS Code 编辑器里按 <strong>F5</strong> 后，会让你选择模拟器来运行 Flutter 程序，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/flutter_run1.gif" alt="Flutter run" width="88%" title="Flutter run" align="center"></p><p>这个是分别在 iOS 和 Android 运行 Flutter 的效果，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/demo.png" alt="no-shadow" title="Flutter run"></p><h2 id="运行-Flutter-案例"><a href="#运行-Flutter-案例" class="headerlink" title="运行 Flutter 案例"></a>运行 Flutter 案例</h2><p>现在所有的都准备好了，您可以去我的 GitHub 上下载上篇文章中的案例代码，也可以 <code>git clone</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> <span class="variable">$HOME</span>/Desktop <span class="comment">#进到桌面</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/persilee/flutter_pro.git <span class="comment">#下载案例</span></span><br><span class="line"><span class="built_in">cd</span> flutter_pro <span class="comment">#进入案例目录</span></span><br><span class="line">flutter packages get <span class="comment">#获取依赖包</span></span><br><span class="line">code ./ <span class="comment">#用 VS Code 打开</span></span><br></pre></td></tr></table></figure><p>完成以上步骤后，在 VS Code 按 F5 选择模拟器，查看运行效果，如图</p><p><img src="https://cdn.lishaoy.net/flutterInstall/flutter_run5.png" alt="no-shadow" title="Flutter Demo"></p><p>好的，大功告成，这篇到处为止，下篇将手拉手带大家完成一个实操小案例 。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/flutterInstall.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Flutter：手拉手带你极速构建漂亮的跨平台移动应用 ✿ 初识</title>
      <link>https://h.lishaoy.net/beautifulFlutter.html</link>
      <guid>https://h.lishaoy.net/beautifulFlutter.html</guid>
      <pubDate>Mon, 05 Nov 2018 02:41:09 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/beautifulFlutter/flutter_750x400.png&quot; class=&quot;full-image&quot; alt=&quot;Flutter&quot; title=&quot;Flutter&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;最近，学习了一些 &lt;code&gt;Flutter&lt;/code&gt; 相关的知识，做了如下的小移动应用，当然是一套代码即可在 &lt;code&gt;iOS&lt;/code&gt; 平台运行,也可以在 &lt;code&gt;Android&lt;/code&gt; 运行。&lt;/p&gt;
&lt;p&gt;下面我将手拉手带您快速构建出漂亮的移动应用界面（如下👇小视频）&lt;/p&gt;
&lt;video id=&quot;flutter&quot; class=&quot;video-js vjs-default-skin&quot; controls preload=&quot;auto&quot; poster=&quot;https://cdn.lishaoy.net/flutter_start/flutter5.1.png&quot; data-setup=&quot;{&#39;example_option&#39;:true}&quot;&gt;
    &lt;source src=&quot;https://cdn.lishaoy.net/flutter_start/flutter7.mp4&quot; type=&quot;video/mp4&quot;&gt;
&lt;/video&gt;

&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/beautifulFlutter/flutter_750x400.png" class="full-image" alt="Flutter" title="Flutter"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>最近，学习了一些 <code>Flutter</code> 相关的知识，做了如下的小移动应用，当然是一套代码即可在 <code>iOS</code> 平台运行,也可以在 <code>Android</code> 运行。</p><p>下面我将手拉手带您快速构建出漂亮的移动应用界面（如下👇小视频）</p><video id="flutter" class="video-js vjs-default-skin" controls preload="auto" poster="https://cdn.lishaoy.net/flutter_start/flutter5.1.png" data-setup="{'example_option':true}">    <source src="https://cdn.lishaoy.net/flutter_start/flutter7.mp4" type="video/mp4"></video><hr><a id="more"></a><h2 id="初识-Flutter"><a href="#初识-Flutter" class="headerlink" title="初识 Flutter"></a>初识 Flutter</h2><p><a href="https://flutterchina.club" target="_blank" rel="noopener">Flutter</a> 是谷歌的移动UI框架，可以快速在iOS和Android上构建高质量的原生用户界面。</p><p>Google 推出 Flutter 移动应用框架已经有三年，直到今年的 Google I/O 开发大会才正式介绍 <code>Flutter</code> 且发布 Beta 版本, <a href="https://www.youtube.com/watch?v=w2TcYP8qiRI" target="_blank" rel="noopener">Flutter Google I/O 视频</a> 这个是 <code>Flutter</code> 在油管（YouTube）的 Google I/O 开发者大会的视频，请自行观看。</p><p>再来看看更加生动的视频介绍，加速您的初识 <code>Flutter</code></p><video id="IntroducingFlutter" class="video-js vjs-default-skin" controls preload="auto" poster="https://cdn.lishaoy.net/beautifulFlutter/IntroducingFlutter1.png" data-setup="{'example_option':true}">    <source src="https://cdn.lishaoy.net/beautifulFlutter/IntroducingFlutter.mp4" type="video/mp4"></video><hr><p>Google 的广告应用 Adwords，阿里的闲鱼 App 都是基于 <code>Flutter</code> 框架开发的。</p><p>以下是阿里巴巴用 Flutter 打造了5000多万用户闲鱼 App (Flutter Developer Story) 的故事视频</p><video id="FlutterDeveloperStory" class="video-js vjs-default-skin" controls preload="auto" poster="https://cdn.lishaoy.net/beautifulFlutter/FlutterDeveloperStory2.png" data-setup="{'example_option':true}">    <source src="https://cdn.lishaoy.net/beautifulFlutter/FlutterDeveloperStory.mp4" type="video/mp4"></video><hr><p><code>Flutter</code> 作为谷歌推出的跨平台开发框架，一经推出便吸引了不少注意,在 GitHub 上的 Star 数已超过 <strong>4W+</strong> 。</p><p><a href="https://www.bilibili.com/video/av27857568/" target="_blank" rel="noopener">CMTC全球大前端技术大会 ㄧ Flutter视频</a> 这是 Google中国在 <strong>bilibili</strong> 上发布的视频，请自行观看。</p><h2 id="Flutter-特性"><a href="#Flutter-特性" class="headerlink" title="Flutter 特性"></a>Flutter 特性</h2><p>通过以上的视频，对于 <code>Flutter</code> 已有所认识，下面再来看看 <code>Flutter</code> 的以下特性</p><h3 id="热重载"><a href="#热重载" class="headerlink" title="热重载"></a>热重载</h3><p>当你修改了代码 <kbd>⌘</kbd> - <kbd>S</kbd> ，可立刻看到效果，而且可以保持界面状态不变（如文本框输入的信息不会改变），如图：</p><p><img src="https://cdn.lishaoy.net/beautifulFlutter/HotReload1.gif" alt="no-shadow" title="Hot reload"></p><h3 id="设计"><a href="#设计" class="headerlink" title="设计"></a>设计</h3><p><code>Flutter</code> 自带 Google 推行的设计系统：<a href="https://www.material.io" target="_blank" rel="noopener">Material Design</a> ，它提供了丰富的 <strong>Material Design</strong> 风格的组件（比如：按钮、输入框、对话框、导航栏、边栏等），而且也提供了丰富的 <strong>iOS（Cupertino）</strong> 风格的组件，利用这些风格的组件能够快速的构建应用，如图</p><p><img src="https://cdn.lishaoy.net/beautifulFlutter/Material.jpg" alt="no-shadow" title="Material Design"></p><h3 id="widget"><a href="#widget" class="headerlink" title="widget"></a>widget</h3><p><strong>widget</strong> 是 <code>Flutter</code> 应用程序基本构建块, <code>Flutter</code> 既不使用 WebView，也不使用操作系统的原生控件，相反 <code>Flutter</code> 使用自己的高性能渲染引擎来绘制<strong>widget</strong> ， <code>Flutter</code> 的中心思想是用 <strong>widget</strong> 构建你的 UI（<strong>一切皆为 widget</strong>） ，如图是官网给出的框架图：</p><p><img src="https://cdn.lishaoy.net/beautifulFlutter/widget.png" alt="no-shadow" title="widget"></p><p>在这个架构里，你可以实现 <code>Flutter</code> 提供的所有现成的 <strong>widget</strong> ，也可以创建自己定制的 <strong>widget</strong> ，每个 <strong>widget</strong> 都是公开的，你可以从高层次且统一的 <strong>widget</strong> 中获得开发效率优势，这个设计的目标是为了用更少的代码做更多的事情。</p><h3 id="语言"><a href="#语言" class="headerlink" title="语言"></a>语言</h3><p><code>Flutter</code> 使用 C、C ++、Dart 和 Skia（2D渲染引擎）技术构建，如上图，底层（engine）是用 C ++ ，框架是用 Dart ，当然我们开发使用的也是 Dart。</p><p>Dart 是 Google 发布的一种高效、简洁、拥有完整类型系统的 <code>结构化的Web编程</code> 语言， <code>Flutter</code> 官方给出为什么选择 Dart 作为开发语言的原因，如下</p><ul><li>开发人员的效率</li><li>面向对象</li><li>可预测，高性能</li><li>快速内存分配</li></ul><p>对于开发者（语言使用者）来说，不必太担心，Dart 和 Java 、 JavaScript 比较类似，有一些程序语言基础，便可拿来即用（不清楚的看看语法、关键字、类型即可）。</p><p>现在想必您对 <code>Flutter</code> 已经有了一定的认识，在之后的篇幅了会手拉手带你极速构如文头小视频的小应用。</p><p><img class="hidden" src="https://cdn.lishaoy.net/beautifulFlutter/flutter_750x400.png" alt="VS Code" width="100%" title="VS Code" align="center"></p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/beautifulFlutter.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>VS Code：让你工作效率翻倍的23个插件和23个编辑技巧</title>
      <link>https://h.lishaoy.net/VSCodeCodingSkills.html</link>
      <guid>https://h.lishaoy.net/VSCodeCodingSkills.html</guid>
      <pubDate>Sat, 13 Oct 2018 07:39:44 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code.jpg&quot; class=&quot;full-image&quot; alt=&quot;VS Code&quot; title=&quot;VS Code&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;总结了一些平时常用且好用的 &lt;strong&gt;VS Code&lt;/strong&gt; 的插件和编辑技巧分享出来。&lt;/p&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code.jpg" class="full-image" alt="VS Code" title="VS Code"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>总结了一些平时常用且好用的 <strong>VS Code</strong> 的插件和编辑技巧分享出来。</p><a id="more"></a><h2 id="外观"><a href="#外观" class="headerlink" title="外观"></a>外观</h2><h3 id="主题"><a href="#主题" class="headerlink" title="主题"></a>主题</h3><p>这里我分享两款主题：</p><ol><li><strong><a href="https://marketplace.visualstudio.com/items?itemName=Equinusocio.vsc-material-theme" target="_blank" rel="noopener">Material Theme</a></strong></li></ol><p>效果如图：</p><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_theme1.jpg" alt="no-shadow" title="Material Theme"></p><ol start="2"><li><strong><a href="https://marketplace.visualstudio.com/items?itemName=dustinsanders.an-old-hope-theme-vscode" target="_blank" rel="noopener">An Old Hope Theme</a></strong></li></ol><p>效果如图：</p><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_theme2.jpg" alt="no-shadow" title="An Old Hope Theme"></p><h3 id="图标"><a href="#图标" class="headerlink" title="图标"></a>图标</h3><ol start="3"><li><strong><a href="https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme" target="_blank" rel="noopener">Material Icon Theme</a></strong>当然，这两款主题的文件管理器（左侧）的 icon 小图标使用的是 Material Icon Theme</li></ol><h3 id="字体及其他"><a href="#字体及其他" class="headerlink" title="字体及其他"></a>字体及其他</h3><p>其他和外观相关的设置如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">&#123;</span><br><span class="line">    <span class="attr">"editor.multiCursorModifier"</span>: <span class="string">"ctrlCmd"</span>,</span><br><span class="line">    <span class="attr">"editor.formatOnPaste"</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">"workbench.activityBar.visible"</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">"workbench.iconTheme"</span>: <span class="string">"eq-material-theme-icons-darker"</span>,</span><br><span class="line">    <span class="attr">"workbench.colorCustomizations"</span>: &#123;&#125;,</span><br><span class="line">    <span class="attr">"materialTheme.cache.workbench.settings"</span>: &#123;</span><br><span class="line">        <span class="attr">"themeColours"</span>: <span class="string">"Darker"</span>,</span><br><span class="line">        <span class="attr">"accentPrevious"</span>: <span class="string">"Acid Lime"</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">"workbench.colorTheme"</span>: <span class="string">"Material Theme Darker"</span>,</span><br><span class="line">    <span class="attr">"material-icon-theme.angular.iconsEnabled"</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">"material-icon-theme.folders.icons"</span>: <span class="string">"specific"</span>,</span><br><span class="line">    <span class="attr">"editor.lineHeight"</span>: <span class="number">24</span>,</span><br><span class="line">    <span class="attr">"editor.fontLigatures"</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">"editor.fontFamily"</span>: <span class="string">"FiraCode-Medium"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>特别注意的是 <code>&quot;editor.lineHeight&quot;: 24,</code> 和 <code>&quot;editor.fontFamily&quot;: &quot;FiraCode-Medium&quot;</code> 。</p><p><code>&quot;editor.lineHeight&quot;: 24,</code> ： 设置代码的行间距，这里比默认的稍大些，就这一点小小的改变，让代码看起来清爽整洁。</p><p><code>&quot;editor.fontFamily&quot;: &quot;FiraCode-Medium&quot;</code> ： 设置字体，这种字体会让代码看起来更形象生动，如下</p><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_code3.png" alt="FiraCode-Medium字体" width="100%" title="FiraCode-Medium字体" align="center"></p><p>红色竖线左边是使用了 <strong>FiraCode-Medium</strong> 字体的效果，红色竖线右边是没有使用 <strong>FiraCode-Medium</strong> 字体的效果</p><p>关于 <strong>FiraCode-Medium</strong> 字体更多效果可查阅 <a href="https://github.com/tonsky/FiraCode" target="_blank" rel="noopener">https://github.com/tonsky/FiraCode</a> 地址。</p><h2 id="代码管理"><a href="#代码管理" class="headerlink" title="代码管理"></a>代码管理</h2><h3 id="格式化"><a href="#格式化" class="headerlink" title="格式化"></a>格式化</h3><ol start="4"><li><strong><a href="https://marketplace.visualstudio.com/items?itemName=HookyQR.beautify" target="_blank" rel="noopener">Beautify</a></strong> ：格式化的时候，给出格式化文本选项，如下</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_Beatify.gif" alt="Beautify" width="100%" title="Beautify" align="center"></p><ol start="5"><li><strong><a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" target="_blank" rel="noopener">Prettier</a></strong> ：个人比较喜欢这个，看起来代码更清晰，如下</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_Prettier.gif" alt="Prettier" width="100%" title="Prettier" align="center"></p><div class="note info"><p>当然，大家可以自定义快捷键，也可以按 <kbd>⌘</kbd> - <kbd>⇧</kbd> - <kbd>P</kbd>  来搜索相关命令 </p></div>  <h3 id="代码检查"><a href="#代码检查" class="headerlink" title="代码检查"></a>代码检查</h3><ol start="6"><li><a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" target="_blank" rel="noopener">ESLint</a> ：检查 <code>js</code> 语法规范，你可以使用不同的规范，如 <a href="https://www.npmjs.com/package/eslint-config-airbnb" target="_blank" rel="noopener">airbnb</a> 、<a href="https://github.com/standard/eslint-config-standard" target="_blank" rel="noopener">standard</a> 、<a href="https://github.com/google/eslint-config-google" target="_blank" rel="noopener">google</a>。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=eg2.tslint" target="_blank" rel="noopener">TSLint</a> ：检查 <code>typescript</code> 语法规范。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=shinnn.stylelint" target="_blank" rel="noopener">Stylelint</a> ：检查 <code>CSS/SCSS/Less</code> 语法规范。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint" target="_blank" rel="noopener">Markdownlint</a> ：检查 <code>markdown</code> 语法规范。</li></ol><h3 id="自动补全"><a href="#自动补全" class="headerlink" title="自动补全"></a>自动补全</h3><p>以下插件点击链接可以查看gif动图，详细了解具体功能。</p><ol start="10"><li><a href="https://emmet.io" target="_blank" rel="noopener">Emmet</a> ：大家应该很熟悉这个插件了（很好用），VS Code 已经内置了，很到位。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-close-tag" target="_blank" rel="noopener">Auto Close Tag</a> ：自动闭合 <code>html</code> 等标签 （&lt;/…&gt;）。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag" target="_blank" rel="noopener">Auto Rename Tag</a> ：修改 <code>html</code> 标签时，自动修改闭合标签。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense" target="_blank" rel="noopener">Path Intellisense</a> ：自动提示补全路径。</li></ol><h3 id="代码片段"><a href="#代码片段" class="headerlink" title="代码片段"></a>代码片段</h3><ol start="14"><li><strong>snippets</strong> ：搭建可以自己安装各种代码片段（vue、react、angular等），这里就不列举。</li></ol><h2 id="功能扩展"><a href="#功能扩展" class="headerlink" title="功能扩展"></a>功能扩展</h2><p>以下的功能扩展插件大部分都有gif动图，可点击链接了解详细功能</p><ol start="15"><li><a href="https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer" target="_blank" rel="noopener">Bracket Pair Colorizer</a> ：让代码的各种括号呈现不同的颜色。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner" target="_blank" rel="noopener">Code Runner</a> ：可以在编辑器里直接运行代码，查看结果。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=anseki.vscode-color" target="_blank" rel="noopener">Color Picker</a> ：可以直接在编辑器里打开色板，选择各种模式的颜色。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=joelday.docthis" target="_blank" rel="noopener">Document This</a> ：可以给函数、类等自动的加上详细的注释。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistory" target="_blank" rel="noopener">Git History</a> ：方便的查看git版本管理的详细信息。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer" target="_blank" rel="noopener">Live Server</a> ：可以一键在本地启动服务器。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync" target="_blank" rel="noopener">Settings Sync</a> ：重点介绍下这个插件，如果你有两台电脑（比如，家里和公司）都使用 VS Code ，可是在公司或家里对 VS Code 安装了插件或者修改了配置，回到家或公司又要重新弄一次，这个插件就能解决问题，同步多台电脑设置。</li></ol><p>只需要把配置上传到GitHub，在另一个地方下载配置即可，如下</p><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_Sync.gif" alt="Settings Sync" width="100%" title="Settings Sync" align="center"></p><ol start="22"><li><a href="https://marketplace.visualstudio.com/items?itemName=rubbersheep.gi" target="_blank" rel="noopener">gi</a> ：可以给 <code>.gitignore</code> 文件添加各种语言忽略文件配置。</li><li><a href="https://marketplace.visualstudio.com/items?itemName=pnp.polacode" target="_blank" rel="noopener">Polacode</a> ：可以把代码生成图片（有些地方发代码结构会乱也没有代码高亮，这时候就可以生成图片再发）。</li></ol><h2 id="编辑技巧"><a href="#编辑技巧" class="headerlink" title="编辑技巧"></a>编辑技巧</h2><h3 id="光标"><a href="#光标" class="headerlink" title="光标"></a>光标</h3><ol><li>把光标移到文件的首部或尾部</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_1.gif" alt="⌘ - ↑ 或 ⌘ - ↓" width="100%" title="⌘ - ↑ 或 ⌘ - ↓" align="center"></p><ol start="2"><li>把光标移动到行的首部或者尾部</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_2.gif" alt="⌘ - ← 或 ⌘ - →" width="100%" title="⌘ - ← 或 ⌘ - →" align="center"></p><ol start="3"><li>按单词移动</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_3.gif" alt="⌥ - ← 或 ⌥ - →" width="100%" title="⌥ - ← 或 ⌥ - →" align="center"></p><ol start="4"><li>按单词大小写分解移动光标</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_4.gif" alt="⌥ - ⌃ - ← 或 ⌥ - ⌃ - →" width="100%" title="⌥ - ⌃ - ← 或 ⌥ - ⌃ - →" align="center"></p><h3 id="选择"><a href="#选择" class="headerlink" title="选择"></a>选择</h3><ol start="5"><li>选择行以上或以下全部内容</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_5.gif" alt="⇧ - ⌘ - ↑ 或 ⇧ - ⌘ - ↓" width="100%" title="⇧ - ⌘ - ↑ 或 ⇧ - ⌘ - ↓" align="center"></p><ol start="6"><li>选择到行首或行尾的内容</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_6.gif" alt="⇧ - ⌘ - ← 或 ⇧ - ⌘ - →" width="100%" title="⇧ - ⌘ - ← 或 ⇧ - ⌘ - →" align="center"></p><ol start="7"><li>按字母或单词选择</li></ol><ul><li><kbd>⇧</kbd> - <kbd>←</kbd> 、 <kbd>⇧</kbd> - <kbd>→</kbd> 按字母选择   </li><li><kbd>⇧</kbd> - <kbd>⌥</kbd> - <kbd>←</kbd> 、 <kbd>⇧</kbd> - <kbd>⌥</kbd> - <kbd>→</kbd> 按单词选择</li></ul><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_7.gif" alt="⇧ - ← 、 ⇧ - → 或 ⇧ - ⌥ - ← 、 ⇧ - ⌥ - →" width="100%" title="⇧ - ← 、 ⇧ - → 或 ⇧ - ⌥ - ← 、⇧ - ⌥ - →" align="center"></p><ol start="8"><li>伸缩选择</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_8.gif" alt="⇧ - ⌃ - ⌘ - ← 或 ⇧ - ⌃ - ⌘ - →" width="100%" title="⇧ - ⌃ - ⌘ - ← 或 ⇧ - ⌃ - ⌘ - →" align="center"></p><ol start="9"><li>选择匹配单词</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_9.gif" alt="⌘ - D 或 ⌘ - U" width="100%" title="⌘ - D 或 ⌘ - U" align="center"></p><h3 id="行"><a href="#行" class="headerlink" title="行"></a>行</h3><ol start="10"><li>向上或向下移动行</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_10.gif" alt="⌥ - ↑ 或 ⌥ - ↓" width="100%" title="⌥ - ↑ 或 ⌥ - ↓" align="center"></p><ol start="11"><li>复制或删除行</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_11.gif" alt="⌥ - ⇧ - ↓ 或 ⌘ - ⇧ - K" width="100%" title="⌥ - ⇧ - ↓ 或 ⌘ - ⇧ - K" align="center"></p><ol start="12"><li>多行合并成一行</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_12.gif" alt="⌘ - J" width="100%" title="⌘ - J" align="center"></p><ol start="13"><li>缩进或伸缩行</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_13.gif" alt="⌘ - [ 或 ⌘ - ]" width="100%" title="⌘ - [ 或 ⌘ - ]" align="center"></p><ol start="14"><li>在当前行之上或下插入行</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_14.gif" alt="⌘ - ↩ 或 ⌘ - ⇧ - ↩" width="100%" title="⌘ - ↩ 或 ⌘ - ⇧ - ↩" align="center"></p><h3 id="多行"><a href="#多行" class="headerlink" title="多行"></a>多行</h3><ol start="15"><li>鼠标点击，多行编辑</li></ol><p>按 <kbd>⌘</kbd> 选择编辑点，按 <kbd>⎋</kbd> 退出多行编辑</p><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_15.gif" alt="⌘" width="100%" title="⌘" align="center"></p><ol start="16"><li>使用快捷键多行编辑</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_16.gif" alt="⌘ - ⌥ - ↓ 或 ⌘ - ⌥ - ↑" width="100%" title="⌘ - ⌥ - ↓ 或 ⌘ - ⌥ - ↑" align="center"></p><ol start="17"><li>在所选择的行的结尾插入编辑点</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_17.gif" alt="⇧ - ⌥ - I" width="100%" title="⇧ - ⌥ - I" align="center"></p><ol start="18"><li>选择栏位</li></ol><p>按 <kbd>⇧</kbd> - <kbd>⌘</kbd> 再选择栏位</p><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_18.gif" alt="⇧ - ⌘" width="100%" title="⇧ - ⌘" align="center"></p><h3 id="高级"><a href="#高级" class="headerlink" title="高级"></a>高级</h3><ol start="19"><li>查看类或方法的定义</li></ol><ul><li>按 <kbd>⌥</kbd> 点击，可以在新页面查看</li><li>按 <kbd>⇧</kbd> - <kbd>⌥</kbd> - <kbd>⌘</kbd> 点击，可以在新组查看</li><li>按 <kbd>⇧</kbd> - <kbd>F12</kbd> 点击，可以在当前页面查看</li></ul><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_19.gif" alt="查看定义" width="100%" title="查看定义" align="center"></p><ol start="20"><li>折叠代码</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_20.gif" alt="⌥ - ⌘ - ] 或 ⌥ - ⌘ - [" width="100%" title="⌥ - ⌘ - ] 或 ⌥ - ⌘ - [" align="center"></p><ol start="21"><li>去掉选择行的尾部空格</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_21.gif" alt="⌘ - K 、 ⌘ - X" width="100%" title="⌘ - K 、 ⌘ - X" align="center"></p><ol start="22"><li>定位到指定行号</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_22.gif" alt="⌃ - G" width="100%" title="⌃ - G" align="center"></p><ol start="23"><li>在文件里查找类或方法</li></ol><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_23.gif" alt="@" width="100%" title="@" align="center"></p><p>最后，如果记不住这些快捷键，可以按 <kbd>⌘</kbd> - <kbd>K</kbd> 、 <kbd>⌘</kbd> - <kbd>S</kbd> 搜索对应快捷键绑定</p><p><img src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code_24.gif" alt="搜索快捷键" width="100%" title="搜索快捷键" align="center"></p><p><img class="hidden" src="https://cdn.lishaoy.net/VSCodeCodingSkills/vs-code.jpg" alt="VS Code" width="100%" title="VS Code" align="center"></p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/VSCodeCodingSkills.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>百度统计</title>
      <link>https://h.lishaoy.net/baidustatistics.html</link>
      <guid>https://h.lishaoy.net/baidustatistics.html</guid>
      <pubDate>Tue, 29 May 2018 19:48:15 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;/images/loading.gif&quot; data-original=&quot;https://cdn.lishaoy.net/baidustatistics/lishaoy.net.990x699.jpg&quot; class=&quot;full-image&quot; alt=&quot;lishaoy.net&quot; title=&quot;lishaoy.net&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;前天，弄了个百度统计，看看数据还挺有意思。。。&lt;/p&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="/images/loading.gif" data-original="https://cdn.lishaoy.net/baidustatistics/lishaoy.net.990x699.jpg" class="full-image" alt="lishaoy.net" title="lishaoy.net"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>前天，弄了个百度统计，看看数据还挺有意思。。。</p><a id="more"></a><p><strong>站点</strong> 接入 <strong>百度统计</strong> ，还是挺简单的：</p><ul><li>首先，去 <a href="https://tongji.baidu.com/web/welcome/login" target="_blank" rel="noopener">百度统计</a> 注册账号，根据提示绑定自己站点域名</li><li>之后，把提供的 <code>js</code> 代码放到自己站点</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//百度统计</span></span><br><span class="line"><span class="keyword">var</span> _hmt = _hmt || [];</span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> hm = <span class="built_in">document</span>.createElement(<span class="string">"script"</span>);</span><br><span class="line">  hm.src = <span class="string">"https://hm.baidu.com/hm.js?5fd52c901f2213883f51a476eab3914b"</span>;</span><br><span class="line">  <span class="keyword">var</span> l = <span class="built_in">document</span>.getElementsByTagName(<span class="string">"script"</span>).length;</span><br><span class="line">  <span class="keyword">var</span> s = <span class="built_in">document</span>.getElementsByTagName(<span class="string">"script"</span>)[l - <span class="number">1</span>];</span><br><span class="line">  s.parentNode.insertBefore(hm, s);</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><p>这些设置好之后，就可以看到统计数据了，来看看 👀 <em>2018-05-29</em> 当天的统计数据情况如何</p><h4 id="今日流量"><a href="#今日流量" class="headerlink" title="今日流量"></a>今日流量</h4><p>纵轴分别是 <em>今日、昨日、预计今日</em> ，横轴分别是 <em>PV、UV、IP、跳出率、平均访问时长</em> 等 （预计今日的数据，不是很靠谱）</p><p><img src="https://cdn.lishaoy.net/baidustatistics/1.png" alt="今日流量" width="100%" title="今日流量" align="center"></p><h4 id="来源网站"><a href="#来源网站" class="headerlink" title="来源网站"></a>来源网站</h4><p>会列出访客从哪儿来到你的<strong>站点</strong>，以及会统计访问时长等</p><p><img src="https://cdn.lishaoy.net/baidustatistics/2.png" alt="来源网站" width="100%" title="来源网站" align="center"></p><h4 id="受欢迎页面"><a href="#受欢迎页面" class="headerlink" title="受欢迎页面"></a>受欢迎页面</h4><p>会统计每个页面的 <em>浏览量、退出次数、平均停留时间</em> 等</p><p><img src="https://cdn.lishaoy.net/baidustatistics/3.png" alt="受欢迎页面" width="100%" title="受欢迎页面" align="center"></p><h4 id="新老访客"><a href="#新老访客" class="headerlink" title="新老访客"></a>新老访客</h4><p>会统计新老访客 <em>浏览量、平均访问时长、平均访问页数</em> 等</p><p><img src="https://cdn.lishaoy.net/baidustatistics/4.png" alt="新老访客" width="100%" title="新老访客" align="center"></p><h4 id="地域分布"><a href="#地域分布" class="headerlink" title="地域分布"></a>地域分布</h4><p>会统计访客来自哪个 <em>国家、省份、城市、网络运营商</em> 等</p><p><img src="https://cdn.lishaoy.net/baidustatistics/5.png" alt="地域分布" width="100%" title="地域分布" align="center"></p><p>这里我只展示部分统计功能，如想了解其它更多功能，可去 <a href="https://tongji.baidu.com/web/welcome/login" target="_blank" rel="noopener">百度统计</a> 网站查看。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/baidustatistics.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>前端性能优化</title>
      <link>https://h.lishaoy.net/webOptimize.html</link>
      <guid>https://h.lishaoy.net/webOptimize.html</guid>
      <pubDate>Thu, 10 May 2018 04:55:59 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;https://cdn.lishaoy.net/webOptimize/Optimize.png&quot; class=&quot;full-image&quot; alt=&quot;web optimize&quot; title=&quot;web optimize&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;关于 &lt;strong&gt;性能优化&lt;/strong&gt; 是个大的面，这篇文章主要涉及到 &lt;strong&gt;前端&lt;/strong&gt; 的几个点，如 &lt;strong&gt;前端性能优化&lt;/strong&gt; 的流程、常见技术手段、工具等。&lt;/p&gt;
&lt;p&gt;提及 &lt;strong&gt;前端性能优化&lt;/strong&gt; ，大家应该都会想到 &lt;strong&gt;雅虎军规&lt;/strong&gt;，本文会结合 &lt;strong&gt;雅虎军规&lt;/strong&gt; 融入自己的了解知识，进行的总结和梳理 😜&lt;/p&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="https://cdn.lishaoy.net/webOptimize/Optimize.png" class="full-image" alt="web optimize" title="web optimize"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>关于 <strong>性能优化</strong> 是个大的面，这篇文章主要涉及到 <strong>前端</strong> 的几个点，如 <strong>前端性能优化</strong> 的流程、常见技术手段、工具等。</p><p>提及 <strong>前端性能优化</strong> ，大家应该都会想到 <strong>雅虎军规</strong>，本文会结合 <strong>雅虎军规</strong> 融入自己的了解知识，进行的总结和梳理 😜</p><a id="more"></a><p>首先，我们先来看看 👀 <strong>雅虎军规</strong> 的 <strong>35</strong> 条。</p><div class="note info"><ol><li>尽量减少 HTTP 请求个数——须权衡</li><li>使用 <strong>CDN</strong>（内容分发网络）</li><li>为文件头指定 Expires 或 Cache-Control ，使内容具有缓存性。</li><li>避免空的 src 和 href</li><li>使用 gzip 压缩内容</li><li>把 CSS 放到顶部</li><li>把 JS 放到底部</li><li>避免使用 CSS 表达式</li><li>将 CSS 和 JS 放到外部文件中</li><li>减少 DNS 查找次数</li><li>精简 CSS 和 JS</li><li>避免跳转</li><li>剔除重复的 JS 和 CSS</li><li>配置 ETags</li><li>使 AJAX 可缓存</li><li>尽早刷新输出缓冲</li><li>使用 GET 来完成 AJAX 请求</li><li>延迟加载</li><li>预加载</li><li>减少 DOM 元素个数</li><li>根据域名划分页面内容</li><li>尽量减少 iframe 的个数</li><li>避免 404</li><li>减少 Cookie 的大小</li><li>使用无 cookie 的域</li><li>减少 DOM 访问</li><li>开发智能事件处理程序</li><li>用 <link> 代替 @import</li><li>避免使用滤镜</li><li>优化图像</li><li>优化 CSS Spirite</li><li>不要在 HTML 中缩放图像——须权衡</li><li>favicon.ico要小而且可缓存</li><li>保持单个内容小于25K</li><li>打包组件成复合文本</li></ol></div>  <p>如对 <strong>雅虎军规</strong> 的具体细则内容不是很了解，可自行去各搜索 🔍 引擎 ，搜索 <strong>雅虎军规</strong> 了解详情。</p><h2 id="压缩-合并"><a href="#压缩-合并" class="headerlink" title="压缩 合并"></a>压缩 合并</h2><p>对于 <strong>前端性能优化</strong> 自然要关注 <strong>首屏</strong> 打开速度，而这个速度，很大因素是花费在网络请求上，那么怎么减少网络请求的时间呢？</p><ul><li>减少网络请求次数 </li><li>减小文件体积</li><li>使用 <code>CDN</code> 加速</li></ul><p>所以 <strong>压缩、合并</strong> 就是一个解决方案，当然可以用 <code>gulp</code> 、 <code>webpack</code> 、 <code>grunt</code> 等构建工具 <strong>压缩、合并</strong></p><h3 id="JS、CSS-压缩-合并"><a href="#JS、CSS-压缩-合并" class="headerlink" title="JS、CSS 压缩 合并"></a><code>JS、CSS</code> 压缩 合并</h3><p>例如：<code>gulp js、css</code> 压缩、合并代码如下 👇</p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#JS、CSS-压缩-合并" target="_blank" rel="noopener">gulpfile.js</a></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></pre></td><td class="code"><pre><span class="line"><span class="comment">//压缩、合并js</span></span><br><span class="line">gulp.task(<span class="string">'scripts'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> gulp.src([</span><br><span class="line">        <span class="string">'./public/lib/fastclick/lib/fastclick.min.js'</span>,</span><br><span class="line">        <span class="string">'./public/lib/jquery_lazyload/jquery.lazyload.js'</span>,</span><br><span class="line">        <span class="string">'./public/lib/velocity/velocity.min.js'</span>,</span><br><span class="line">        <span class="string">'./public/lib/velocity/velocity.ui.min.js'</span>,</span><br><span class="line">        <span class="string">'./public/lib/fancybox/source/jquery.fancybox.pack.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/utils.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/motion.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/scrollspy.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/post-details.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/bootstrap.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/push.js'</span>,</span><br><span class="line">        <span class="string">'./public/live2dw/js/perTips.js'</span>,</span><br><span class="line">        <span class="string">'./public/live2dw/lib/L2Dwidget.min.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/love.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/busuanzi.pure.mini.js'</span>,</span><br><span class="line">        <span class="string">'./public/js/src/activate-power-mode.js'</span></span><br><span class="line">    ]).pipe(concat(<span class="string">'all.js'</span>)).pipe(minify()).pipe(gulp.dest(<span class="string">'./public/dist/'</span>));</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 压缩、合并 CSS </span></span><br><span class="line">gulp.task(<span class="string">'css'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> gulp.src([</span><br><span class="line">        <span class="string">'./public/lib/font-awesome/css/font-awesome.min.css'</span>,</span><br><span class="line">        <span class="string">'./public/lib/fancybox/source/jquery.fancybox.css'</span>,</span><br><span class="line">        <span class="string">'./public/css/main.css'</span>,</span><br><span class="line">        <span class="string">'./public/css/lib.css'</span>,</span><br><span class="line">        <span class="string">'./public/live2dw/css/perTips.css'</span></span><br><span class="line">    ]).pipe(concat(<span class="string">'all.css'</span>)).pipe(minify()).pipe(gulp.dest(<span class="string">'./public/dist/'</span>));</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>然后，再把 <strong>压缩、合并</strong> 的 <code>JS、CSS</code> 放入 <code>CDN</code> , 👀 看看效果如何</p><p>如图：<em> <strong>压缩、合并</strong> 且放入 <code>CND</code> 之后的效果 </em></p><p><img src="https://cdn.lishaoy.net/webOptimize/concatJs.png" alt="首页请求速度(js)" width="100%" title="首页请求速度(js)" align="center"></p><p><img src="https://cdn.lishaoy.net/webOptimize/concatCss.png" alt="首页请求速度(css)" width="100%" title="首页请求速度(css)" align="center"></p><p>以上是 <a href="https://lishaoy.net" target="_blank" rel="noopener">lishaoy.net</a> 清除缓存后的 <strong>首页</strong> 请求速度。</p><p>可见，请求时间是 <strong>4.59 s</strong> ，总请求个数 <strong>51</strong> ， 而 <code>js</code> 的请求个数是 <strong>8</strong> ，<code>css</code> 的请求个数是 <strong>3</strong> <em>（其实就 all.css 一个，其它 2 个是 Google浏览器加载的）</em>， 而没使用 <strong>压缩、合并</strong> 时候，请求时间是 <strong>10</strong> 多秒，总请求个数有 <strong>70</strong> 多个，<code>js</code> 的请求个数是 <strong>20</strong> 多个 ，对比请求时间 <strong>性能</strong> 提升 <strong>1倍</strong> 多</p><p>如图：<em>有缓存下的首页效果</em></p><p><img src="https://cdn.lishaoy.net/webOptimize/concatJs2.png" alt="首页请求速度（缓存）" width="100%" title="首页请求速度（缓存）" align="center"></p><p>基本都是秒开 😝</p><div class="note warning"><p><em>Tips：在 <strong>压缩、合并</strong> 后，单个文件控制在 25 ~ 30 KB左右，同一个域下，最好不要多于5个资源</em></p></div> <h3 id="图片压缩、合并"><a href="#图片压缩、合并" class="headerlink" title="图片压缩、合并"></a>图片压缩、合并</h3><p>例如：<code>gulp</code> 图片压缩代码如下 👇</p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#图片压缩、合并" target="_blank" rel="noopener">gulpfile.js</a></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="comment">//压缩image</span></span><br><span class="line">gulp.task(<span class="string">'imagemin'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    gulp.src(<span class="string">'./public/**/*.&#123;png,jpg,gif,ico,jpeg&#125;'</span>)</span><br><span class="line">        .pipe(imagemin())</span><br><span class="line">        .pipe(gulp.dest(<span class="string">'./public'</span>));</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>图片的合并可以采用 <code>CSS Spirite</code>，方法就是把一些小图用 <code>PS</code> 合成一张图，用 <code>css</code> 定位显示每张图片的位置</p><figure class="highlight css"><figcaption><span>css</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="selector-class">.top_right</span> <span class="selector-class">.phone</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="built_in">url</span>(../images/top_right.png) no-repeat <span class="number">7px</span> -<span class="number">17px</span>;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">38px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.top_right</span> <span class="selector-class">.help</span> &#123;</span><br><span class="line">    <span class="attribute">background</span>: <span class="built_in">url</span>(../images/top_right.png) no-repeat <span class="number">0</span> -<span class="number">47px</span>;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">38px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，把 <strong>压缩</strong> 的图片放入 <code>CDN</code> , 👀 看看，效果如何</p><p><img src="https://cdn.lishaoy.net/webOptimize/minImages.png" alt="首页请求速度（images）" width="100%" title="首页请求速度（images）" align="center"></p><p>可见，请求时间是 <strong>1.70 s</strong> ,总请求个数 <strong>50</strong> ， 而 <code>img</code> 的请求个数是 <strong>15</strong> <em>（这里因为首页都是大图，就没有合并，只是压缩了）</em> ，但是，效果很好 😀 ，从 <strong>4.59 s</strong> 缩短到 <strong>1.70 s</strong>, 性能又提升一倍。</p><p>再看看有缓存情况如何 😏</p><p><img src="https://cdn.lishaoy.net/webOptimize/minImages1.png" alt="首页请求速度（images 缓存）" width="100%" title="首页请求速度（images 缓存）" align="center"></p><p>请求时间是 <strong>1.05 s</strong> ，有缓存和无缓存基本差不多</p><div class="note warning"><p><em>Tips：大的图片在不同终端，应该使用不同分辨率，而不应该使用缩放（百分比）</em></p></div> <p>整个 <strong>压缩、合并</strong> <em>（js、css、img）</em> 再放入 <code>CDN</code> ，请求时间从 <strong>10</strong> 多秒 ，到最后的 <strong>1.70 s</strong> ，性能提升 <strong>5</strong> 倍多，可见，这个操作必要性。</p><hr><h2 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h2><p>缓存会根据请求保存输出内容的副本，例如 <strong>页面、图片、文件</strong>，当下一个请求来到的时候:如果是相同的<code>URL</code>，缓存直接使 用本地的副本响应访问请求，而不是向源服务器再次发送请求。因此，可以从以下 <strong>2</strong> 个方面提升性能。</p><ul><li>减少相应延迟，提升响应时间</li><li>减少网络带宽消耗，节省流量</li></ul><p>我们用两幅图来了解下浏览器的 <strong>缓存机制</strong></p><p><strong>浏览器第一次请求</strong></p><p><img src="https://cdn.lishaoy.net/webOptimize/webCache3.png" alt="no-shadow" title="第一次请求"></p><p><strong>浏览器再次请求</strong></p><p><img src="https://cdn.lishaoy.net/webOptimize/webCache4.png" alt="no-shadow" title="再次请求"></p><p>从以上两幅图中，可以清楚的了解浏览器 <strong>缓存</strong> 的过程。首次访问一个 <code>URL</code> ，没有 <strong>缓存</strong> ，但是，服务器会响应一些 <code>header</code> 信息，如：<code>expires、cache-control、last-modified、etag</code> 等，来记录下次请求是否缓存、如何缓存。再次访问这个 <code>URL</code> 时候，浏览器会根据首次访问返回的 <code>header</code> 信息，来决策是否缓存、如何缓存。我们重点来分析下第二幅图，其实是分两条线路，如下 👇</p><ul><li><strong>第一条线路：</strong> 当浏览器再次访问某个 <code>URL</code> 时，会先获取资源的 <code>header</code> 信息，判断是否命中强缓存 <em>（cache-control和expires）</em> ，如命中，直接从缓存获取资源，包括响应的 <code>header</code> 信息 <em>（请求不会和服务器通信）</em> ，也就是 <strong>强缓存</strong> ，如图</li></ul><p><img src="https://cdn.lishaoy.net/webOptimize/webCache2.png" alt="强缓存" width="100%" title="强缓存" align="center"></p><ul><li><strong>第二条线路：</strong> 如没有命中 <strong>强缓存</strong> ，浏览器会发送请求到服务器，请求会携带第一次请求返回的有关缓存的 <code>header</code> 信息 <em>（Last-Modified/If-Modified-Since和Etag/If-None-Match）</em> ，由服务器根据请求中的相关 <code>header</code> 信息来比对结果是否协商缓存命中；若命中，则服务器返回新的响应 <code>header</code> 信息更新缓存中的对应 <code>header</code> 信息，但是并不返回资源内容，它会告知浏览器可以直接从缓存获取；否则返回最新的资源内容，也就是 <strong>协商缓存</strong>。</li></ul><p>现在，我们了解到浏览器缓存机制分为 <strong>强缓存、协商缓存</strong>，再来看看他们的区别 👇</p><table><thead><tr><th style="text-align:center">缓存策略</th><th style="text-align:center">获取资源形式</th><th style="text-align:center">状态码</th><th style="text-align:center">发送请求到服务器</th></tr></thead><tbody><tr><td style="text-align:center">强缓存</td><td style="text-align:center">从缓存取</td><td style="text-align:center">200（from memory cache）</td><td style="text-align:center">否，直接从缓存取</td></tr><tr><td style="text-align:center">协商缓存</td><td style="text-align:center">从缓存取</td><td style="text-align:center">304（not modified）</td><td style="text-align:center">是，通过服务器来告知缓存是否可用</td></tr></tbody></table><h3 id="强缓存"><a href="#强缓存" class="headerlink" title="强缓存"></a>强缓存</h3><p>与强缓存相关的 <code>header</code> 字段有两个：</p><h4 id="expires"><a href="#expires" class="headerlink" title="expires"></a>expires</h4><p><strong>expires：</strong> 这是 <code>http1.0</code> 时的规范，它的值为一个绝对时间的 <strong>GMT</strong> 格式的时间字符串，如 <code>Mon, 10 Jun 2015 21:31:12 GMT</code> ，如果发送请求的时间在 <strong>expires</strong> 之前，那么本地缓存始终有效，否则就会发送请求到服务器来获取资源</p><h4 id="cache-control"><a href="#cache-control" class="headerlink" title="cache-control"></a>cache-control</h4><p><strong>cache-control:</strong> <code>max-age=number</code> ，这是 <code>http1.1</code> 时出现的 <code>header</code> 信息，主要是利用该字段的 <code>max-age</code> 值来进行判断，它是一个相对值；资源第一次的请求时间和 <strong>Cache-Control</strong> 设定的有效期，计算出一个资源过期时间，再拿这个过期时间跟当前的请求时间比较，如果请求时间在过期时间之前，就能命中缓存，否则未命中， <strong>cache-control</strong> 除了该字段外，还有下面几个比较常用的设置值：</p><ul><li><strong>no-cache：</strong> 不使用本地缓存。需要使用缓存协商，先与服务器确认返回的响应是否被更改，如果之前的响应中存在 <code>ETag</code> ，那么请求的时候会与服务端验证，如果资源未被更改，则可以避免重新下载。</li><li><strong>no-store：</strong> 直接禁止游览器缓存数据，每次用户请求该资源，都会向服务器发送一个请求，每次都会下载完整的资源。</li><li><strong>public：</strong> 可以被所有的用户缓存，包括终端用户和 <code>CDN</code> 等中间代理服务器。</li><li><strong>private：</strong> 只能被终端用户的浏览器缓存，不允许 <code>CDN</code> 等中继缓存服务器对其缓存。</li></ul><div class="note warning"><p><em>Tips：如果 cache-control 与 expires 同时存在的话，cache-control 的优先级高于 expires</em></p></div> <h3 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h3><p>协商缓存都是由浏览器和服务器协商，来确定是否缓存，协商主要通过下面两组 <code>header</code> 字段，这两组字段都是成对出现的，即第一次请求的响应头带上某个字段 <em>（ <strong>Last-Modified</strong> 或者 <strong>Etag</strong> ）</em> ，则后续请求会带上对应的请求字段 <em>（<strong>If-Modified-Since</strong> 或者 <strong>If-None-Match</strong> ）</em> ，若响应头没有 <strong>Last-Modified</strong> 或者 <strong>Etag</strong> 字段，则请求头也不会有对应的字段。</p><h4 id="Last-Modified-If-Modified-Since"><a href="#Last-Modified-If-Modified-Since" class="headerlink" title="Last-Modified/If-Modified-Since"></a>Last-Modified/If-Modified-Since</h4><p>二者的值都是 <code>GMT</code> 格式的时间字符串，具体过程：</p><ul><li><p>浏览器第一次跟服务器请求一个资源，服务器在返回这个资源的同时，在 <code>respone</code> 的 <code>header</code> 加上 <strong>Last-Modified</strong> 字段，这个 <code>header</code> 字段表示这个资源在服务器上的最后修改时间</p></li><li><p>浏览器再次跟服务器请求这个资源时，在 <code>request</code> 的 <code>header</code> 上加上 <strong>If-Modified-Since</strong> 字段，这个 <code>header</code> 字段的值就是上一次请求时返回的 <strong>Last-Modified</strong> 的值</p></li><li><p>服务器再次收到资源请求时，根据浏览器传过来 <strong>If-Modified-Since</strong> 和资源在服务器上的最后修改时间判断资源是否有变化，如果没有变化则返回 <code>304 Not Modified</code> ，但是不会返回资源内容；如果有变化，就正常返回资源内容。当服务器返回 <code>304 Not Modified</code> 的响应时，<code>response header</code> 中不会再添加 <strong>Last-Modified的header</strong> ，因为既然资源没有变化，那么 <strong>Last-Modified</strong> 也就不会改变，这是服务器返回 <code>304</code> 时的 <code>response header</code></p></li><li><p>浏览器收到 <code>304</code> 的响应后，就会从缓存中加载资源</p></li><li><p>如果协商缓存没有命中，浏览器直接从服务器加载资源时，<strong>Last-Modified</strong> 的 <code>Header</code> 在重新加载的时候会被更新，下次请求时，<strong>If-Modified-Since</strong> 会启用上次返回的<strong>Last-Modified</strong> 值</p></li></ul><h4 id="Etag-If-None-Match"><a href="#Etag-If-None-Match" class="headerlink" title="Etag/If-None-Match"></a>Etag/If-None-Match</h4><p>这两个值是由服务器生成的每个资源的唯一标识字符串，只要资源有变化就这个值就会改变；其判断过程与 <strong>Last-Modified、If-Modified-Since</strong> 类似，与 <strong>Last-Modified</strong> 不一样的是，当服务器返回 <code>304 Not Modified</code> 的响应时，由于 <strong>ETag</strong> 重新生成过，<code>response header</code> 中还会把这个 <strong>ETag</strong> 返回，即使这个 <strong>ETag</strong> 跟之前的没有变化。</p><div class="note warning"><p><em>Tips：Last-Modified与ETag是可以一起使用的，服务器会优先验证ETag，一致的情况下，才会继续比对Last-Modified，最后才决定是否返回304。</em></p></div> <h3 id="Service-Worker"><a href="#Service-Worker" class="headerlink" title="Service Worker"></a>Service Worker</h3><h4 id="什么是-Service-Worker"><a href="#什么是-Service-Worker" class="headerlink" title="什么是 Service Worker"></a>什么是 Service Worker</h4><blockquote><p><strong>Service Worker</strong> 本质上充当Web应用程序与浏览器之间的代理服务器，也可以在网络可用时作为浏览器和网络间的代理。它们旨在（除其他之外）使得能够创建有效的离线体验，拦截网络请求并基于网络是否可用以及更新的资源是否驻留在服务器上来采取适当的动作。他们还允许访问推送通知和后台同步API。</p><footer><strong>Service Worker API</strong><cite><a href="http://sethgodin.typepad.com/seths_blog/2009/07/welcome-to-island-marketing.html" target="_blank" rel="noopener">developer.mozilla.org</a></cite></footer></blockquote><p><br><strong>Service worker</strong> 可以解决目前离线应用的问题，同时也可以做更多的事。 <strong>Service Worker</strong> 可以使你的应用先访问本地缓存资源，所以在离线状态时，在没有通过网络接收到更多的数据前，仍可以提供基本的功能（一般称之为 Offline First）。这是原生APP 本来就支持的功能，这也是相比于 <code>web app</code> ，原生 <code>app</code> 更受青睐的主要原因</p><p>再来看看 👀 <strong>service worker</strong> 能做些什么</p><ul><li>后台消息传递</li><li>网络代理，转发请求，伪造响应</li><li>离线缓存</li><li>消息推送</li><li>…</li></ul><div class="note success"><p><strong><em>本文主要以（<a href="https://lishaoy.net" target="_blank" rel="noopener">lishaoy.net</a>）资源缓存为例,阐述下 service worker如何工作</em></strong></p></div>  <h4 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h4><p><strong>service worker</strong> 初次安装的生命周期，如图 🌠</p><p><img src="https://cdn.lishaoy.net/webOptimize/serviceWorker.png" alt="no-shadow" title="sw生命周期"></p><p>从上 👆 图可知，<strong>service worker</strong> 工作的流程：</p><ol><li><strong>安装：</strong>  <code>service worker URL</code> 通过 <code>serviceWorkerContainer.register()</code> 来获取和注册。</li><li><strong>激活：</strong> 当 <code>service worker</code> 安装完成后，会接收到一个激活事件(activate event)。 <code>onactivate</code> 主要用途是清理先前版本的 <code>service worker</code> 脚本中使用的资源。</li><li><strong>监听：</strong> 两种状态<ul><li>终止以节省内存；</li><li>监听获取 <code>fetch</code> 和消息 <code>message</code> 事件。</li></ul></li><li><strong>销毁：</strong> 是否销毁由浏览器决定，如果一个 <code>service worker</code> 长期不使用或者机器内存有限，则可能会销毁这个 <code>worker</code> 。</li></ol><div class="note warning"><p><em>Tips：激活成功之后，在 Chrome 浏览器里，可以访问 chrome://inspect/#service-workers和 chrome://serviceworker-internals/ 可以查看到当前运行的service worker ，如图 👇。</em></p></div> <p><img src="https://cdn.lishaoy.net/webOptimize/serviceWorker1.png" alt="service worker" width="100%" title="service worker" align="center"></p><p><strong>现在，我们来写个简单的例子 🌰 </strong></p><h4 id="注册-service-worker"><a href="#注册-service-worker" class="headerlink" title="注册 service worker"></a>注册 service worker</h4><p>要安装 <code>service worker</code> ，你需要在你的页面上注册它。这个步骤告诉浏览器你的 <code>service worker</code> 脚本在哪里。</p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#注册-service-worker" target="_blank" rel="noopener">app.js</a></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">if</span> (<span class="string">'serviceWorker'</span> <span class="keyword">in</span> navigator) &#123;</span><br><span class="line">  navigator.serviceWorker.register(<span class="string">'/sw.js'</span>).then(<span class="function"><span class="keyword">function</span>(<span class="params">registration</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// Registration was successful</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'ServiceWorker registration successful with scope: '</span>,    registration.scope);</span><br><span class="line">  &#125;).catch(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// registration failed :(</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'ServiceWorker registration failed: '</span>, err);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码检查 <code>service worker API</code> 是否可用，如果可用，<code>service worker /sw.js</code> 被注册。如果这个 <code>service worker</code> 已经被注册过，浏览器会自动忽略上面的代码。</p><h4 id="激活-service-worker"><a href="#激活-service-worker" class="headerlink" title="激活 service worker"></a>激活 service worker</h4><p>在你的 <code>service worker</code> 注册之后，浏览器会尝试为你的页面或站点安装并激活它。 <code>install</code> 事件会在安装完成之后触发。<code>install</code> 事件一般是被用来填充你的浏览器的离线缓存能力。你需要为 <code>install</code> 事件定义一个 <code>callback</code> ，并决定哪些文件你想要缓存.</p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#激活-service-worker" target="_blank" rel="noopener">sw.js</a></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">// The files we want to cache</span></span><br><span class="line"><span class="keyword">var</span> CACHE_NAME = <span class="string">'my-site-cache-v1'</span>;</span><br><span class="line"><span class="keyword">var</span> urlsToCache = [</span><br><span class="line">  <span class="string">'/'</span>,</span><br><span class="line">  <span class="string">'/css/main.css'</span>,</span><br><span class="line">  <span class="string">'/js/main.js'</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line">self.addEventListener(<span class="string">'install'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Perform install steps</span></span><br><span class="line">  event.waitUntil(</span><br><span class="line">    caches.open(CACHE_NAME)</span><br><span class="line">      .then(<span class="function"><span class="keyword">function</span>(<span class="params">cache</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'Opened cache'</span>);</span><br><span class="line">        <span class="keyword">return</span> cache.addAll(urlsToCache);</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>install callback</code> 中，我们需要执行以下步骤：</p><ul><li>开启一个缓存</li><li>缓存我们的文件</li><li>决定是否所有的资源是否要被缓存</li></ul><p>上面的代码中，我们通过 <code>caches.open</code> 打开我们指定的 <code>cache</code> 文件名，然后我们调用 <code>cache.addAll</code> 并传入我们的文件数组。这是通过一连串 <code>promise</code> <em>（caches.open 和 cache.addAll）</em> 完成的。<code>event.waitUntil</code> 拿到一个 <code>promise</code> 并使用它来获得安装耗费的时间以及是否安装成功。</p><h4 id="监听-service-worker"><a href="#监听-service-worker" class="headerlink" title="监听 service worker"></a>监听 service worker</h4><p>现在我们已经将你的站点资源缓存了，你需要告诉 <code>service worker</code> 让它用这些缓存内容来做点什么。有了 <code>fetch</code> 事件，这是很容易做到的。</p><p>每次任何被 <code>service worker</code> 控制的资源被请求到时，都会触发 <code>fetch</code> 事件，我们可以给 <code>service worker</code> 添加一个 <code>fetch</code> 的事件监听器，接着调用 <code>event</code> 上的 <code>respondWith()</code> 方法来劫持我们的 <strong>HTTP</strong> 响应，然后你用可以用自己的方法来更新他们。</p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#监听-service-worker" target="_blank" rel="noopener">sw.js</a></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">self.addEventListener(<span class="string">'fetch'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  event.respondWith(</span><br><span class="line">    caches.match(event.request);</span><br><span class="line">  );</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p><code>caches.match(event.request)</code> 允许我们对网络请求的资源和 <code>cache</code> 里可获取的资源进行匹配，查看是否缓存中有相应的资源。这个匹配通过 <code>url</code> 和 <code>vary header</code> 进行，就像正常的 <strong>HTTP</strong> 请求一样。</p><p>那么，我们如何返回 <code>request</code> 呢，下面 👇 就是一个例子 🌰</p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#监听-service-worker" target="_blank" rel="noopener">sw.js</a></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">self.addEventListener(<span class="string">'fetch'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>&#123;</span><br><span class="line">  event.respondWith(</span><br><span class="line">    caches.match(event.request)</span><br><span class="line">      .then(<span class="function"><span class="keyword">function</span>(<span class="params">response</span>) </span>&#123;</span><br><span class="line">        <span class="comment">// Cache hit - return response</span></span><br><span class="line">        <span class="keyword">if</span> (response) &#123;</span><br><span class="line">          <span class="keyword">return</span> response;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> fetch(event.request);</span><br><span class="line">      &#125;</span><br><span class="line">    )</span><br><span class="line">  );</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>上面的代码里我们定义了 <code>fetch</code> 事件，在 <code>event.respondWith</code> 里，我们传入了一个由 <code>caches.match</code> 产生的 <code>promise.caches.match</code> 查找 <code>request</code> 中被 <code>service worker</code> 缓存命中的 <code>response</code> 。如果我们有一个命中的 <code>response</code> ，我们返回被缓存的值，否则我们返回一个实时从网络请求 <code>fetch</code> 的结果。</p><h4 id="sw-toolbox"><a href="#sw-toolbox" class="headerlink" title="sw-toolbox"></a>sw-toolbox</h4><div class="note success"><p><em>当然，我也可以使用第三方库，例如：<a href="https://lishaoy.net" target="_blank" rel="noopener">lishaoy.net</a> 使用了 <strong>sw-toolbox</strong>。</em></p></div><p><strong>sw-toolbox</strong> 使用非常简单，下面 👇 就是 <a href="https://lishaoy.net" target="_blank" rel="noopener">lishaoy.net</a> 的一个例子 🌰</p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#sw-toolbox" target="_blank" rel="noopener">persilee.js</a></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="string">"serviceWorker"</span> <span class="keyword">in</span> navigator ? navigator.serviceWorker.register(<span class="string">'/sw.js'</span>).then(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  navigator.serviceWorker.controller ? <span class="built_in">console</span>.log(<span class="string">"Assets cached by the controlling service worker."</span>) : <span class="built_in">console</span>.log(<span class="string">"Please reload this page to allow the service worker to handle network operations."</span>)</span><br><span class="line">&#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"ERROR: "</span> + e)</span><br><span class="line">&#125;) : <span class="built_in">console</span>.log(<span class="string">"Service workers are not supported in the current browser."</span>)</span><br></pre></td></tr></table></figure><p>以上是 <strong>注册</strong> 一个 <code>service woker</code></p><figure class="highlight javascript"><figcaption><span>javascript</span><a href="https://lishaoy.net/webOptimize.html#sw-toolbox" target="_blank" rel="noopener">sw.js</a></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></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> cacheVersion = <span class="string">"20180527"</span>;</span><br><span class="line">    <span class="keyword">var</span> staticImageCacheName = <span class="string">"image"</span> + cacheVersion;</span><br><span class="line">    <span class="keyword">var</span> staticAssetsCacheName = <span class="string">"assets"</span> + cacheVersion;</span><br><span class="line">    <span class="keyword">var</span> contentCacheName = <span class="string">"content"</span> + cacheVersion;</span><br><span class="line">    <span class="keyword">var</span> vendorCacheName = <span class="string">"vendor"</span> + cacheVersion;</span><br><span class="line">    <span class="keyword">var</span> maxEntries = <span class="number">100</span>;</span><br><span class="line">    self.importScripts(<span class="string">"/lib/sw-toolbox/sw-toolbox.js"</span>);</span><br><span class="line">    self.toolbox.options.debug = <span class="literal">false</span>;</span><br><span class="line">    self.toolbox.options.networkTimeoutSeconds = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">    self.toolbox.router.get(<span class="string">"/images/(.*)"</span>, self.toolbox.cacheFirst, &#123;</span><br><span class="line">        cache: &#123;</span><br><span class="line">            name: staticImageCacheName,</span><br><span class="line">            maxEntries: maxEntries</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    self.toolbox.router.get(<span class="string">'/js/(.*)'</span>, self.toolbox.cacheFirst, &#123;</span><br><span class="line">        cache: &#123;</span><br><span class="line">            name: staticAssetsCacheName,</span><br><span class="line">            maxEntries: maxEntries</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    self.toolbox.router.get(<span class="string">'/css/(.*)'</span>, self.toolbox.cacheFirst, &#123;</span><br><span class="line">        cache: &#123;</span><br><span class="line">            name: staticAssetsCacheName,</span><br><span class="line">            maxEntries: maxEntries</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    self.addEventListener(<span class="string">"install"</span>, <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> event.waitUntil(self.skipWaiting())</span><br><span class="line">    &#125;);</span><br><span class="line">    self.addEventListener(<span class="string">"activate"</span>, <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> event.waitUntil(self.clients.claim())</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><p>就这样搞定了 🍉 <em>(具体的用法可以去 <a href="https://googlechromelabs.github.io/sw-toolbox/api.html#main" target="_blank" rel="noopener">sw-toolbox</a> 查看)</em></p><p>有的同学就问，<code>service worker</code> 这么好用，这个缓存空间到底是多大？其实，在  <strong>Chrome</strong> 可以看到，如图</p><p><img src="https://cdn.lishaoy.net/webOptimize/storageQuota.png" alt="fstorage quota" width="100%" title="storage quota" align="center"></p><p>可以看到，大概有 <strong>30G</strong> ，我的站点只用了 <strong>183MB</strong> ，完全够用了 🍓</p><p>最后，来两张图 </p><p><img src="https://cdn.lishaoy.net/webOptimize/serviceWorker2.png" alt="from ServiceWorker" width="100%" title="from ServiceWorker" align="center"></p><p><img src="https://cdn.lishaoy.net/webOptimize/serviceWorker3.png" alt="Cache Storage" width="100%" title="Cache Storage" align="center"></p><p><img class="hidden" src="https://cdn.lishaoy.net/webOptimize/Optimize.png" alt="web optimize" width="100%" title="web optimize" align="center"></p><h2 id="未完，待续。。。-😜"><a href="#未完，待续。。。-😜" class="headerlink" title="未完，待续。。。 😜"></a>未完，待续。。。 😜</h2>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/webOptimize.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>Promise诞生记</title>
      <link>https://h.lishaoy.net/createPromise.html</link>
      <guid>https://h.lishaoy.net/createPromise.html</guid>
      <pubDate>Tue, 17 Apr 2018 17:34:09 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;https://cdn.lishaoy.net/createPromise/promise.png&quot; class=&quot;full-image&quot; alt=&quot;Promise&quot; title=&quot;Promise&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;前端近年的兴起，有大部分是因为 &lt;code&gt;NodeJS&lt;/code&gt; 的诞生，而 &lt;code&gt;NodeJS&lt;/code&gt; 是个适用于 &lt;strong&gt;异步IO&lt;/strong&gt; 密集型的语言，一些基于 &lt;code&gt;NodeJS&lt;/code&gt; 的框架，比喻 &lt;em&gt;KOA2、Adonis&lt;/em&gt; 就有大量的 &lt;code&gt;async&lt;/code&gt; 和 &lt;code&gt;await&lt;/code&gt; 语法，&lt;code&gt;async&lt;/code&gt;的函数的返回值就是 &lt;code&gt;Promise&lt;/code&gt; 对象，我们可以用 &lt;code&gt;async&lt;/code&gt; 和 &lt;code&gt;await&lt;/code&gt; 语法，写出优雅的异步代码，来替换难看且难维护的回调函数。&lt;/p&gt;
&lt;p&gt;这里我们会渐进式的来创建一个 &lt;code&gt;Promise&lt;/code&gt; 的实现，如果，你还不了解 &lt;code&gt;Promise&lt;/code&gt; ，赶快移步 &lt;a href=&quot;http://es6.ruanyifeng.com/#docs/promise&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Promise&lt;/a&gt; 了解学习，当然这个实现会符合 &lt;a href=&quot;https://promisesaplus.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Promise/A+&lt;/a&gt; 规范，&lt;code&gt;JavaScript&lt;/code&gt; 中有很多第三方的 &lt;code&gt;Promise&lt;/code&gt; 库，&lt;a href=&quot;http://bluebirdjs.com/docs/getting-started.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;bluebird&lt;/a&gt; 就是一个第三方 &lt;code&gt;Promise&lt;/code&gt; 类库，相比其它第三方类库或标准对象来说，其有以下优点：功能更齐全而不臃肿、浏览器兼容性更好,大家可以了解下。&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="https://cdn.lishaoy.net/createPromise/promise.png" class="full-image" alt="Promise" title="Promise"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>前端近年的兴起，有大部分是因为 <code>NodeJS</code> 的诞生，而 <code>NodeJS</code> 是个适用于 <strong>异步IO</strong> 密集型的语言，一些基于 <code>NodeJS</code> 的框架，比喻 <em>KOA2、Adonis</em> 就有大量的 <code>async</code> 和 <code>await</code> 语法，<code>async</code>的函数的返回值就是 <code>Promise</code> 对象，我们可以用 <code>async</code> 和 <code>await</code> 语法，写出优雅的异步代码，来替换难看且难维护的回调函数。</p><p>这里我们会渐进式的来创建一个 <code>Promise</code> 的实现，如果，你还不了解 <code>Promise</code> ，赶快移步 <a href="http://es6.ruanyifeng.com/#docs/promise" target="_blank" rel="noopener">Promise</a> 了解学习，当然这个实现会符合 <a href="https://promisesaplus.com" target="_blank" rel="noopener">Promise/A+</a> 规范，<code>JavaScript</code> 中有很多第三方的 <code>Promise</code> 库，<a href="http://bluebirdjs.com/docs/getting-started.html" target="_blank" rel="noopener">bluebird</a> 就是一个第三方 <code>Promise</code> 类库，相比其它第三方类库或标准对象来说，其有以下优点：功能更齐全而不臃肿、浏览器兼容性更好,大家可以了解下。</p><hr><a id="more"></a><p>废话不多说，直接开干。。。 😠</p><h2 id="定义-Promise-类型"><a href="#定义-Promise-类型" class="headerlink" title="定义 Promise 类型"></a>定义 Promise 类型</h2><p>一个简单 <code>Promise</code> 语法，如下</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ... doSomething</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (<span class="comment">/* 异步操作成功 */</span>)&#123;</span><br><span class="line">    resolve(value);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    reject(error);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">promise.then(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// success</span></span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// failure</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="实现-resolve-和-then"><a href="#实现-resolve-和-then" class="headerlink" title="实现 resolve 和 then"></a>实现 resolve 和 then</h3><p>首先我们以上 👆 的语法，自己定义一个 <code>Promise</code> 实例</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">function</span> <span class="title">Promise</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> callback = <span class="literal">null</span>;</span><br><span class="line">  <span class="comment">//实现 then 方法 , 先一步一步来，实现传一个参数 -- resolve</span></span><br><span class="line">  <span class="keyword">this</span>.then = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">    callback = cb;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//实现 resolve , value:异步操作的最终值</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">resolve</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">    callback(value);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">//执行 function 参数</span></span><br><span class="line">  fn(resolve);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一个简单的实例写好了，然后，来用一下,看看 👀 结果如何</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">const</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve</span>)</span>&#123;</span><br><span class="line">  resolve(<span class="number">66</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">p.then(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(value);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><div class="note info"><p>执行结果是：<code>callback is not a function</code></p></div><h3 id="修改-callback-为异步"><a href="#修改-callback-为异步" class="headerlink" title="修改 callback 为异步"></a>修改 callback 为异步</h3><p><strong>这里就遇到一个问题： </strong>  </p><p><strong> 发现 <code>resolve()</code> 在 <code>then()</code> 之前调用，在 <code>resolve()</code> 被调用的时候， <code>callback</code> 还是 <code>null</code> ，我们的代码是同步的，而不是异步的。<br> 如是，想办法解决掉这个问题，就是利用 <code>setTimeout</code> , 把 <code>callback</code> 加入异步队列</strong> </p><p>代码如下 👇</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">function</span> <span class="title">Promise</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> callback = <span class="literal">null</span>;</span><br><span class="line">  <span class="comment">//实现 then 方法 , 先一步一步来，实现传一个参数 -- resolve</span></span><br><span class="line">  <span class="keyword">this</span>.then = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>&#123;</span><br><span class="line">    callback = cb;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//实现 resolve , value:异步操作的最终值</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">resolve</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 用 setTimeout 把 callback 加入到异步队列，这样就会，先执行 then() 方法</span></span><br><span class="line">    setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">      callback(value);</span><br><span class="line">    &#125;,<span class="number">1</span>)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">//执行 function 参数</span></span><br><span class="line">  fn(resolve);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后，再来用一下,看看 👀 结果如何</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">const</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve</span>)</span>&#123;</span><br><span class="line">  resolve(<span class="number">66</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">p.then(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(value);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><div class="note info"><p>执行结果是：<code>66</code></p></div><h2 id="未完待续。。。"><a href="#未完待续。。。" class="headerlink" title="未完待续。。。"></a>未完待续。。。</h2>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/createPromise.html#disqus_thread</comments>
    </item>
    
    <item>
      <title>CSSModules</title>
      <link>https://h.lishaoy.net/CSSModules.html</link>
      <guid>https://h.lishaoy.net/CSSModules.html</guid>
      <pubDate>Sat, 07 Apr 2018 01:58:11 GMT</pubDate>
      <description>
      
        &lt;span itemprop=&quot;image&quot; itemscope=&quot;&quot; itemtype=&quot;http://schema.org/ImageObject&quot;&gt;&lt;img itemprop=&quot;url image&quot; src=&quot;https://cdn.lishaoy.net/CSSModules/cssModules.png&quot; class=&quot;full-image&quot; alt=&quot;CSS Modules&quot; title=&quot;CSS Modules&quot;&gt;&lt;meta itemprop=&quot;width&quot; content=&quot;auto&quot;&gt;&lt;meta itemprop=&quot;height&quot; content=&quot;auto&quot;&gt;&lt;/span&gt;
&lt;p&gt;这篇文章来一起了解 &lt;code&gt;css&lt;/code&gt; 模块化的用法和原理 ，&lt;strong&gt;dome&lt;/strong&gt; 地址：&lt;a href=&quot;https://github.com/persilee/webpack_test&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;css modules&lt;/a&gt; 😮&lt;/p&gt;
&lt;hr&gt;
      
      </description>
      
      <content:encoded><![CDATA[<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="https://cdn.lishaoy.net/CSSModules/cssModules.png" class="full-image" alt="CSS Modules" title="CSS Modules"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span><p>这篇文章来一起了解 <code>css</code> 模块化的用法和原理 ，<strong>dome</strong> 地址：<a href="https://github.com/persilee/webpack_test" target="_blank" rel="noopener">css modules</a> 😮</p><hr><a id="more"></a><h4 id="局部作用域"><a href="#局部作用域" class="headerlink" title="局部作用域"></a>局部作用域</h4><p>一般我们引入页面的 <code>CSS</code> 的作用域都是全局的，都是对这个页面起作用，产生局部的作用域，就是使用一个独一无二的 <code>class</code> 的名称，不会和其它选择器重名的， <strong><code>CSS Modules</code></strong> 就是这个原理。下面我们看一段代码</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">import</span> $ <span class="keyword">from</span> <span class="string">'jquery'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./main.css'</span>;</span><br><span class="line"><span class="keyword">import</span> test <span class="keyword">from</span> <span class="string">'./test.html'</span>;</span><br><span class="line"></span><br><span class="line">$(<span class="string">'body'</span>).append($(<span class="string">'&lt;div&gt;&lt;h1&gt;我会变绿&lt;/h1&gt;&lt;/div&gt;'</span>));</span><br><span class="line">$(<span class="string">'div h1'</span>).addClass(styles.testGreen);</span><br><span class="line">$(<span class="string">'body'</span>).append(test).find(<span class="string">'h2'</span>).addClass(styles.testBlue);</span><br></pre></td></tr></table></figure><p>上面的代码我把 <code>main.css</code> 输入到 <code>style</code> 对象，然后下面用了 <code>styles.testGreen</code> 对象的属性形式调用，就会应用 <code>main.css</code> 里的样式</p><figure class="highlight css"><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="selector-class">.testGreen</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: green;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>构建工具（ <code>webpack</code> ）编译成一个哈希字符串</p><figure class="highlight vbscript-html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;div&gt;</span><br><span class="line">    &lt;h1 class="_305zeUSoiGREv3GqPa9H8F"&gt;我会变绿&lt;/h1&gt;</span><br><span class="line">&lt;/div&gt;</span><br></pre></td></tr></table></figure><p><code>main.css</code> 也会同时编译</p><figure class="highlight css"><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="selector-class">._305zeUSoiGREv3GqPa9H8F</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#aaf200</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样一来，这个类名就是独一无二的了，只对应用的组件有效。<strong><code>CSS Modules</code></strong> 支持不同的构建工具，这里我使用的是 <code>webpack</code> ,下文都是以 <code>webpack</code> 为例。</p><p>下面我们来看下 <strong><code>webpack.config.js</code></strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="built_in">module</span>.exports = &#123;</span><br><span class="line">  context: __dirname + <span class="string">'/src'</span>,</span><br><span class="line">  devtool: <span class="string">'eval-source-map'</span>, <span class="comment">//配置生成Source Maps，选择合适的选项</span></span><br><span class="line">  entry: &#123;</span><br><span class="line">    app: [<span class="string">'./app.js'</span>, <span class="string">'./test.js'</span>],</span><br><span class="line">  &#125;,</span><br><span class="line">  output: &#123;</span><br><span class="line">    path: __dirname + <span class="string">'/dist'</span>,</span><br><span class="line">    filename: <span class="string">'bundle.js'</span>,</span><br><span class="line">    publicPath: <span class="string">'/assets'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    loaders: [</span><br><span class="line">      &#123;<span class="attr">test</span>: <span class="regexp">/\.json$/</span>,<span class="attr">loader</span>: <span class="string">'json-loader'</span>&#125;,</span><br><span class="line">      &#123;<span class="attr">test</span>: <span class="regexp">/\.js$/</span>,<span class="attr">exclude</span>: <span class="regexp">/node_modules/</span>,<span class="attr">loader</span>: <span class="string">'babel-loader'</span>&#125;,</span><br><span class="line">      &#123;<span class="attr">test</span>: <span class="regexp">/\.css$/</span>,<span class="attr">loader</span>: ExtractTextPlugin.extract(&#123;</span><br><span class="line">          fallbackLoader: <span class="string">"style-loader"</span>,</span><br><span class="line">          loader: &#123;</span><br><span class="line">            loader: <span class="string">"css-loader"</span>,</span><br><span class="line">            query: &#123;</span><br><span class="line">              modules: <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">      &#123;<span class="attr">test</span>: <span class="regexp">/\.html$/</span>,<span class="attr">loader</span>: <span class="string">'html-loader'</span>&#125;,</span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'style.css'</span>)</span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>上面的代码可以看到，<code>query:{modules:true}</code> 代表开启 <strong><code>CSS Modules</code></strong> 模块，这里还配置了把所以得 <code>css</code> 合并一个文件，具体的可以了解 <code>webpack</code> 的 <code>extract-text-webpack-plugin</code>插件。</p><h4 id="全局作用域"><a href="#全局作用域" class="headerlink" title="全局作用域"></a>全局作用域</h4><p><strong><code>CSS Modules</code></strong> 允许用 <code>:global(.className)</code> 的语法声明一个全局的作用域。加了 <code>:global</code> 的不会被编译成哈希值。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="selector-pseudo">:global(.title)</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: black;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.title</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>test.js</code> 使用普通的写法，就会引用全局的 <code>.title</code> 的样式</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> $ <span class="keyword">from</span> <span class="string">'jquery'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'./main.css'</span>;</span><br><span class="line"><span class="keyword">import</span> test <span class="keyword">from</span> <span class="string">'./test.html'</span>;</span><br><span class="line"></span><br><span class="line">$(<span class="string">'body'</span>).append($(<span class="string">'&lt;div&gt;&lt;h1&gt;我是title&lt;/h1&gt;&lt;/div&gt;'</span>));</span><br><span class="line">$(<span class="string">'div h1'</span>).addClass(<span class="string">'title'</span>);</span><br></pre></td></tr></table></figure><p>结果 <code>h1</code> 的title显示黑色。</p><h4 id="Class的组合"><a href="#Class的组合" class="headerlink" title="Class的组合"></a>Class的组合</h4><p>在 <strong><code>CSS Modules</code></strong> 里，一个选择器可以继承另一个选择器。</p><p>在 <code>mian.css</code> 里，我让 <code>.testBlue</code> 继承 <code>.testBg</code> 类</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="selector-class">.testBg</span> &#123;</span><br><span class="line">  <span class="attribute">background-color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.testBlue</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: blue;</span><br><span class="line">  <span class="attribute">composes</span>: testBg;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不用修改 <code>test.js</code> ,应用了 <code>.testBlue</code> 就会有一个红色的背景。</p><p>编译结果：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="selector-class">.eh33VC37uFHXkCZ8LfKYd</span> &#123;</span><br><span class="line">  <span class="attribute">background-color</span>: <span class="number">#ff0000</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.xrmZso54fTBX29J9G65Ai</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="number">#0c77f8</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>相应的 <code>html</code> 代码：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">h3</span> <span class="attr">class</span>=<span class="string">"xrmZso54fTBX29J9G65Ai eh33VC37uFHXkCZ8LfKYd _2gsuNWm9029FHPYJP62C-t"</span>&gt;</span></span><br><span class="line">    我会变蓝</span><br><span class="line"><span class="tag">&lt;/<span class="name">h3</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="输入变量"><a href="#输入变量" class="headerlink" title="输入变量"></a>输入变量</h4><p><strong><code>CSS Modules</code></strong> 支持使用变量，不过要安装 <strong>PsotCSS</strong> 和 <strong>postcss-modules-values</strong>。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install --save postcss-loader postcss-modules-<span class="built_in">values</span></span><br></pre></td></tr></table></figure><p>把 <code>postcss-loader</code> 加入 <code>webpack.config.js</code> .</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><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">&#123;</span><br><span class="line">    test: <span class="regexp">/\.css$/</span>,</span><br><span class="line">    loader: <span class="string">'style-loader!css-loader?modules!postcss-loader'</span></span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><p>然后我在 <code>colors.css</code> 里定义了一些变量。</p><figure class="highlight css"><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">value</span> blue: #<span class="number">0</span>c77f8;</span><br><span class="line">@<span class="keyword">value</span> red: #ff0000;</span><br><span class="line">@<span class="keyword">value</span> green: #aaf200;</span><br></pre></td></tr></table></figure><p>在 <code>main.css</code> 里可以这样引用变量</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">value</span> colors: <span class="string">"./color.css"</span>;</span><br><span class="line">@<span class="keyword">value</span> blue, red, green from colors;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.title</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.testBg</span> &#123;</span><br><span class="line">  <span class="attribute">background-color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.testGreen</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: green;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.testBlue</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: blue;</span><br><span class="line">  <span class="attribute">composes</span>: testBg;</span><br><span class="line">  <span class="attribute">composes</span>: div;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样就可以把 <code>colors.css</code> 的变量拿过来用了，是不是很神奇。</p>]]></content:encoded>
      
      <comments>https://h.lishaoy.net/CSSModules.html#disqus_thread</comments>
    </item>
    
  </channel>
</rss>
