<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>brightlightkim</title>
    <link>https://brightlightkim.tistory.com/</link>
    <description>Algorithms and Data Structures Research Blog
https://www.linkedin.com/in/taeyang-kim/
https://github.com/brightlightkim</description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 23:29:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>brightlightkim</managingEditor>
    <image>
      <title>brightlightkim</title>
      <url>https://tistory1.daumcdn.net/tistory/5306094/attach/06f63706e08143a283343ddaf69a1da9</url>
      <link>https://brightlightkim.tistory.com</link>
    </image>
    <item>
      <title>How to put images on top of box using TailwindCSS and Reat?</title>
      <link>https://brightlightkim.tistory.com/376</link>
      <description>&lt;pre id=&quot;code_1687036746862&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;section className=&quot;mb-6&quot;&amp;gt;
        &amp;lt;h2 className=&quot;text-center text-5xl font-semibold mb-20&quot;&amp;gt;
          EyeCanKnow's algorithms
        &amp;lt;/h2&amp;gt;
        &amp;lt;div className=&quot;flex px-10 relative&quot;&amp;gt;
          &amp;lt;img
            src={camera_image_url}
            className=&quot;flex-none absolute top-10 left-4 rounded-2xl mb-12 w-1/2 h-96 object-cover&quot;
            alt=&quot;camera&quot;
          /&amp;gt;
          &amp;lt;div className=&quot;w-full h-96 mb-20&quot;&amp;gt;&amp;lt;/div&amp;gt;
          &amp;lt;div className=&quot;w-full h-96 mb-20&quot;&amp;gt;&amp;lt;/div&amp;gt;
          &amp;lt;div className=&quot;lg:pl-32 xl:pl-20 flex items-center bg-gray-200 rounded-xl text-2xl text-gray-700&quot;&amp;gt;
            &amp;lt;p className=&quot;pr-8&quot;&amp;gt;
              EyeCanKnow utilizes the &amp;lt;b&amp;gt;camera&amp;lt;/b&amp;gt; on a test taker's mobile
              device to{&quot; &quot;}
              &amp;lt;u className=&quot;underline-offset-4 decoration-red-400&quot;&amp;gt;
                measure their deception
              &amp;lt;/u&amp;gt;{&quot; &quot;}
              about past experiences via changes in involuntary eye behavior,
              pupil diameter, eye movement, blinks, fixations, and other things.
            &amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/section&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Use Relative on Parent Instance and put Absolute class instead of fixed. Fixed is only use for navigations or bottoms.&lt;/p&gt;</description>
      <category>React/Next.js</category>
      <category>CSS</category>
      <category>react</category>
      <category>tailwindcss</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/376</guid>
      <comments>https://brightlightkim.tistory.com/376#entry376comment</comments>
      <pubDate>Sun, 18 Jun 2023 06:20:00 +0900</pubDate>
    </item>
    <item>
      <title>Window Selection Object</title>
      <link>https://brightlightkim.tistory.com/375</link>
      <description>&lt;h1 style=&quot;background-color: #1b1b1b; color: #ffffff; text-align: start;&quot;&gt;Selection&lt;/h1&gt;
&lt;div style=&quot;background-color: #1b1b1b; color: #ffffff; text-align: start;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Selection&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;object represents the range of text selected by the user or the current position of the caret. To obtain a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Selection&lt;span&gt;&amp;nbsp;&lt;/span&gt;object for examination or manipulation, call&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection&quot;&gt;window.getSelection()&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A user may make a selection from left to right (in document order) or right to left (reverse of document order). The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;anchor&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is where the user began the selection and the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;focus&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is where the user ends the selection. If you make a selection with a desktop mouse, the anchor is placed where you pressed the mouse button, and the focus is placed where you released the mouse button.&lt;/p&gt;
&lt;div id=&quot;sect1&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Note:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Anchor&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;focus&lt;span&gt;&amp;nbsp;&lt;/span&gt;should not be confused with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;start&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;end&lt;span&gt;&amp;nbsp;&lt;/span&gt;positions of a selection. The anchor can be placed before the focus or vice versa, depending on the direction you made your selection.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;instance_properties&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#instance_properties&quot;&gt;Instance properties&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/anchorNode&quot;&gt;Selection.anchorNode&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Read only&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node&quot;&gt;Node&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in which the selection begins. Can return&lt;span&gt;&amp;nbsp;&lt;/span&gt;null&lt;span&gt;&amp;nbsp;&lt;/span&gt;if selection never existed in the document (e.g., an iframe that was never clicked on).&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/anchorOffset&quot;&gt;Selection.anchorOffset&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Read only&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns a number representing the offset of the selection's anchor within the&lt;span&gt;&amp;nbsp;&lt;/span&gt;anchorNode. If&lt;span&gt;&amp;nbsp;&lt;/span&gt;anchorNode&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a text node, this is the number of characters within anchorNode preceding the anchor. If&lt;span&gt;&amp;nbsp;&lt;/span&gt;anchorNode&lt;span&gt;&amp;nbsp;&lt;/span&gt;is an element, this is the number of child nodes of the&lt;span&gt;&amp;nbsp;&lt;/span&gt;anchorNode&lt;span&gt;&amp;nbsp;&lt;/span&gt;preceding the anchor.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/focusNode&quot;&gt;Selection.focusNode&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Read only&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node&quot;&gt;Node&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in which the selection ends. Can return&lt;span&gt;&amp;nbsp;&lt;/span&gt;null&lt;span&gt;&amp;nbsp;&lt;/span&gt;if selection never existed in the document (for example, in an&lt;span&gt;&amp;nbsp;&lt;/span&gt;iframe&lt;span&gt;&amp;nbsp;&lt;/span&gt;that was never clicked on).&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/focusOffset&quot;&gt;Selection.focusOffset&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Read only&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns a number representing the offset of the selection's anchor within the&lt;span&gt;&amp;nbsp;&lt;/span&gt;focusNode. If&lt;span&gt;&amp;nbsp;&lt;/span&gt;focusNode&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a text node, this is the number of characters within&lt;span&gt;&amp;nbsp;&lt;/span&gt;focusNode&lt;span&gt;&amp;nbsp;&lt;/span&gt;preceding the focus. If&lt;span&gt;&amp;nbsp;&lt;/span&gt;focusNode&lt;span&gt;&amp;nbsp;&lt;/span&gt;is an element, this is the number of child nodes of the&lt;span&gt;&amp;nbsp;&lt;/span&gt;focusNode&lt;span&gt;&amp;nbsp;&lt;/span&gt;preceding the focus.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/isCollapsed&quot;&gt;Selection.isCollapsed&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Read only&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns a Boolean indicating whether the selection's start and end points are at the same position.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/rangeCount&quot;&gt;Selection.rangeCount&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Read only&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns the number of ranges in the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/type&quot;&gt;Selection.type&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Read only&lt;/span&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns a string describing the type of the current selection.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;instance_methods&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#instance_methods&quot;&gt;Instance methods&lt;/a&gt;&lt;/h2&gt;
&lt;div&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/addRange&quot;&gt;Selection.addRange()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range&quot;&gt;Range&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;object that will be added to the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapse&quot;&gt;Selection.collapse()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collapses the current selection to a single point.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapseToEnd&quot;&gt;Selection.collapseToEnd()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collapses the selection to the end of the last range in the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapseToStart&quot;&gt;Selection.collapseToStart()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collapses the selection to the start of the first range in the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/containsNode&quot;&gt;Selection.containsNode()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Indicates if a certain node is part of the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/deleteFromDocument&quot;&gt;Selection.deleteFromDocument()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deletes the selection's content from the document.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/extend&quot;&gt;Selection.extend()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Moves the focus of the selection to a specified point.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/getRangeAt&quot;&gt;Selection.getRangeAt()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range&quot;&gt;Range&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;object representing one of the ranges currently selected.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/modify&quot;&gt;Selection.modify()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Changes the current selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/removeRange&quot;&gt;Selection.removeRange()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Removes a range from the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/removeAllRanges&quot;&gt;Selection.removeAllRanges()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Removes all ranges from the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/selectAllChildren&quot;&gt;Selection.selectAllChildren()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Adds all the children of the specified node to the selection.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/setBaseAndExtent&quot;&gt;Selection.setBaseAndExtent()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sets the selection to be a range including all or parts of two specified DOM nodes, and any content located between them.&lt;/p&gt;
&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/toString&quot;&gt;Selection.toString()&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Returns a string currently being represented by the selection object, i.e. the currently selected text.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;notes&quot; data-ke-size=&quot;size26&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#notes&quot;&gt;Notes&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;string_representation_of_a_selection&quot; data-ke-size=&quot;size23&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#string_representation_of_a_selection&quot;&gt;String representation of a selection&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Calling the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/toString&quot;&gt;Selection.toString()&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;method returns the text contained within the selection, e.g.:&lt;/p&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;pre class=&quot;dart&quot; style=&quot;background-color: #000000;&quot;&gt;&lt;code&gt;const selObj = window.getSelection();
window.alert(selObj);
&lt;/code&gt;&lt;/pre&gt;
&lt;span&gt;Copy to Clipboard&lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Note that using a selection object as the argument to&lt;span&gt;&amp;nbsp;&lt;/span&gt;window.alert&lt;span&gt;&amp;nbsp;&lt;/span&gt;will call the object's&lt;span&gt;&amp;nbsp;&lt;/span&gt;toString&lt;span&gt;&amp;nbsp;&lt;/span&gt;method.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;multiple_ranges_in_a_selection&quot; data-ke-size=&quot;size23&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#multiple_ranges_in_a_selection&quot;&gt;Multiple ranges in a selection&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A selection object represents the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range&quot;&gt;Range&lt;/a&gt;s that the user has selected. Typically, it holds only one range, accessed as follows:&lt;/p&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;pre class=&quot;ebnf&quot; style=&quot;background-color: #000000;&quot;&gt;&lt;code&gt;const selObj = window.getSelection();
const range = selObj.getRangeAt(0);
&lt;/code&gt;&lt;/pre&gt;
&lt;span&gt;Copy to Clipboard&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;selObj&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a Selection object&lt;/li&gt;
&lt;li&gt;range&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range&quot;&gt;Range&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;object&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.w3.org/TR/selection-api/#h_note_15&quot;&gt;Selection API specification notes&lt;/a&gt;, the Selection API was initially created by Netscape and allowed multiple ranges (for instance, to allow the user to select a column from a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table&quot;&gt;&amp;lt;table&amp;gt;&lt;/a&gt;). However, browsers other than Gecko did not implement multiple ranges, and the specification also requires the selection to always have a single range.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;selection_and_input_focus&quot; data-ke-size=&quot;size23&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#selection_and_input_focus&quot;&gt;Selection and input focus&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Selection and input focus (indicated by&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement&quot;&gt;Document.activeElement&lt;/a&gt;) have a complex relationship that varies by browser. In cross-browser compatible code, it's better to handle them separately.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Safari and Chrome (unlike Firefox) currently focus the element containing selection when modifying the selection programmatically; it's possible that this may change in the future (see&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.w3.org/Bugs/Public/show_bug.cgi?id=14383&quot;&gt;W3C bug 14383&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://webkit.org/b/38696&quot;&gt;Webkit bug 38696&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;behavior_of_selection_api_in_terms_of_editing_host_focus_changes&quot; data-ke-size=&quot;size23&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#behavior_of_selection_api_in_terms_of_editing_host_focus_changes&quot;&gt;Behavior of Selection API in terms of editing host focus changes&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The Selection API has a common behavior (i.e., shared between browsers) that governs how focus behavior changes for&lt;span&gt;&amp;nbsp;&lt;/span&gt;editing hosts&lt;span&gt;&amp;nbsp;&lt;/span&gt;after certain methods are called.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The behavior is as follows:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;An editing host gains focus if the previous selection was outside of it.&lt;/li&gt;
&lt;li&gt;A Selection API method is called, causing a new selection to be made with the selection range inside the editing host.&lt;/li&gt;
&lt;li&gt;Focus then moves to the editing host.&lt;/li&gt;
&lt;/ol&gt;
&lt;div id=&quot;sect2&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Note:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;The Selection API methods may only move focus to an editing host, not to other focusable elements (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a&quot;&gt;&amp;lt;a&amp;gt;&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The above behavior applies to selections made using the following methods:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapse&quot;&gt;Selection.collapse()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapseToStart&quot;&gt;Selection.collapseToStart()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/collapseToEnd&quot;&gt;Selection.collapseToEnd()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/extend&quot;&gt;Selection.extend()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/selectAllChildren&quot;&gt;Selection.selectAllChildren()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/addRange&quot;&gt;Selection.addRange()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection/setBaseAndExtent&quot;&gt;Selection.setBaseAndExtent()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And when the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range&quot;&gt;Range&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is modified using the following methods:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/setStart&quot;&gt;Range.setStart()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/setEnd&quot;&gt;Range.setEnd()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/setStartBefore&quot;&gt;Range.setStartBefore()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/setStartAfter&quot;&gt;Range.setStartAfter()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/setEndBefore&quot;&gt;Range.setEndBefore()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/setEndAfter&quot;&gt;Range.setEndAfter()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/collapse&quot;&gt;Range.collapse()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/selectNode&quot;&gt;Range.selectNode()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range/selectNodeContents&quot;&gt;Range.selectNodeContents()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3 id=&quot;glossary&quot; data-ke-size=&quot;size23&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection#glossary&quot;&gt;Glossary&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Other key terms used in this section.&lt;/p&gt;
anchor
&lt;p data-ke-size=&quot;size16&quot;&gt;The anchor of a selection is the beginning point of the selection. When making a selection with a mouse, the anchor is where in the document the mouse button is initially pressed. As the user changes the selection using the mouse or the keyboard, the anchor does not move.&lt;/p&gt;
editing host
&lt;p data-ke-size=&quot;size16&quot;&gt;An editable element (e.g., an HTML element with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#contenteditable&quot;&gt;contenteditable&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;set, or the HTML child of a document that has&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/designMode&quot;&gt;designMode&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enabled).&lt;/p&gt;
focus of a selection
&lt;p data-ke-size=&quot;size16&quot;&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;focus&lt;span&gt;&amp;nbsp;&lt;/span&gt;of a selection is the end point of the selection. When making a selection with a mouse, the focus is where in the document the mouse button is released. As the user changes the selection using the mouse or the keyboard, the focus is the end of the selection that moves.&lt;/p&gt;
&lt;div id=&quot;sect3&quot; style=&quot;background-color: #000000; color: #000000;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Note:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;This is not the same as the focused&lt;span&gt;&amp;nbsp;&lt;/span&gt;element&lt;span&gt;&amp;nbsp;&lt;/span&gt;of the document, as returned by&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement&quot;&gt;document.activeElement&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
range
&lt;p data-ke-size=&quot;size16&quot;&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;range&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a contiguous part of a document. A range can contain entire nodes as well as portions of nodes (such as a portion of a text node). A user will normally only select a single range at a time, but it's possible for a user to select multiple ranges (e.g., by using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;Control&lt;span&gt;&amp;nbsp;&lt;/span&gt;key). A range can be retrieved from a selection as a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Range&quot;&gt;range&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;object. Range objects can also be created via the DOM and programmatically added or removed from a selection.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Selection&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/Selection&lt;/a&gt;&lt;/p&gt;</description>
      <category>Bug/JavaScript</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/375</guid>
      <comments>https://brightlightkim.tistory.com/375#entry375comment</comments>
      <pubDate>Wed, 19 Apr 2023 04:42:59 +0900</pubDate>
    </item>
    <item>
      <title>NextJS Navbar with TailwindCSS</title>
      <link>https://brightlightkim.tistory.com/374</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Create responsive navbar in next js using useState hook and tailwind.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import Head from 'next/head';
import Link from 'next/link';
import { useState } from 'react';


export default function Home() {
  const [navbar, setNavbar] = useState(false);
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Create Next Responsive Navbar With Tailwind CSS&amp;lt;/title&amp;gt;
        &amp;lt;meta
          name=&quot;description&quot;
          content=&quot;Create Next JS Responsive Menu with Tailwind CSS&quot;
        /&amp;gt;
        &amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;nav className=&quot;w-full bg-gray-800 shadow&quot;&amp;gt;
        &amp;lt;div className=&quot;justify-between px-4 mx-auto lg:max-w-7xl md:items-center md:flex md:px-8&quot;&amp;gt;
          &amp;lt;div&amp;gt;
            &amp;lt;div className=&quot;flex items-center justify-between py-3 md:py-5 md:block&quot;&amp;gt;
              &amp;lt;a href=&quot;#&quot;&amp;gt;
                &amp;lt;h2 className=&quot;text-2xl text-white font-bold&quot;&amp;gt;NEXT JS&amp;lt;/h2&amp;gt;
              &amp;lt;/a&amp;gt;
              &amp;lt;div className=&quot;md:hidden&quot;&amp;gt;
                &amp;lt;button
                  className=&quot;p-2 text-gray-700 rounded-md outline-none focus:border-gray-400 focus:border&quot;
                  onClick={() =&amp;gt; setNavbar(!navbar)}
                &amp;gt;
                  {navbar ? (
                    &amp;lt;svg
                      xmlns=&quot;http://www.w3.org/2000/svg&quot;
                      className=&quot;w-6 h-6 text-white&quot;
                      viewBox=&quot;0 0 20 20&quot;
                      fill=&quot;currentColor&quot;
                    &amp;gt;
                      &amp;lt;path
                        fillRule=&quot;evenodd&quot;
                        d=&quot;M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z&quot;
                        clipRule=&quot;evenodd&quot;
                      /&amp;gt;
                    &amp;lt;/svg&amp;gt;
                  ) : (
                    &amp;lt;svg
                      xmlns=&quot;http://www.w3.org/2000/svg&quot;
                      className=&quot;w-6 h-6 text-white&quot;
                      fill=&quot;none&quot;
                      viewBox=&quot;0 0 24 24&quot;
                      stroke=&quot;currentColor&quot;
                      strokeWidth={2}
                    &amp;gt;
                      &amp;lt;path
                        strokeLinecap=&quot;round&quot;
                        strokeLinejoin=&quot;round&quot;
                        d=&quot;M4 6h16M4 12h16M4 18h16&quot;
                      /&amp;gt;
                    &amp;lt;/svg&amp;gt;
                  )}
                &amp;lt;/button&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div&amp;gt;
            &amp;lt;div
              className={`flex-1 justify-self-center pb-3 mt-8 md:block md:pb-0 md:mt-0 ${
                navbar ? 'block' : 'hidden'
              }`}
            &amp;gt;
              &amp;lt;ul className=&quot;items-center justify-center space-y-8 md:flex md:space-x-6 md:space-y-0&quot;&amp;gt;
                &amp;lt;li className=&quot;text-white&quot;&amp;gt;
                  &amp;lt;Link href=&quot;/&quot;&amp;gt;
                    &amp;lt;a&amp;gt;Home&amp;lt;/a&amp;gt;
                  &amp;lt;/Link&amp;gt;
                &amp;lt;/li&amp;gt;
                &amp;lt;li className=&quot;text-white&quot;&amp;gt;
                  &amp;lt;Link href=&quot;/blogs&quot;&amp;gt;
                    &amp;lt;a&amp;gt;Blogs&amp;lt;/a&amp;gt;
                  &amp;lt;/Link&amp;gt;
                &amp;lt;/li&amp;gt;
                &amp;lt;li className=&quot;text-white&quot;&amp;gt;
                  &amp;lt;Link href=&quot;/about&quot;&amp;gt;
                    &amp;lt;a&amp;gt;About US&amp;lt;/a&amp;gt;
                  &amp;lt;/Link&amp;gt;
                &amp;lt;/li&amp;gt;
                &amp;lt;li className=&quot;text-white&quot;&amp;gt;
                  &amp;lt;Link href=&quot;/contact&quot;&amp;gt;
                    &amp;lt;a&amp;gt;Contact US&amp;lt;/a&amp;gt;
                  &amp;lt;/Link&amp;gt;
                &amp;lt;/li&amp;gt;
              &amp;lt;/ul&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;div className=&quot;flex justify-center items-center mt-8&quot;&amp;gt;
        &amp;lt;h1 className=&quot;text-2xl font-bold text-purple-500&quot;&amp;gt;
          Create Responsive Navbar Menu in Next js with Tailwind CSS
        &amp;lt;/h1&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React/Next.js</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/374</guid>
      <comments>https://brightlightkim.tistory.com/374#entry374comment</comments>
      <pubDate>Fri, 27 Jan 2023 16:54:05 +0900</pubDate>
    </item>
    <item>
      <title>An introduction to Convolutional Neural Networks</title>
      <link>https://brightlightkim.tistory.com/373</link>
      <description>&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;h2 id=&quot;d8a0&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;Describing what Convolutional Neural Networks are, how they function, how they can be used and why they are so powerful&lt;/h2&gt;
&lt;/div&gt;
&lt;p id=&quot;980e&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;A Convolutional neural network (CNN) is a neural network that has one or more convolutional layers and are used mainly for image processing, classification, segmentation and also for other auto correlated data.&lt;/p&gt;
&lt;p id=&quot;f002&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;A convolution is essentially sliding a filter over the input. One helpful way to think about convolutions is this quote from Dr Prasad Samarakoon: &amp;ldquo;A convolution can be thought as &amp;ldquo;looking at a function&amp;rsquo;s surroundings to make better/accurate predictions of its outcome.&amp;rdquo;&lt;/p&gt;
&lt;p id=&quot;541c&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Rather than looking at an entire image at once to find certain features it can be more effective to look at smaller portions of the image.&lt;/p&gt;
&lt;h2 id=&quot;bd89&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;Common uses for CNNs&lt;/h2&gt;
&lt;p id=&quot;2420&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;The most common use for CNNs is image classification, for example identifying satellite images that contain roads or classifying hand written letters and digits. There are other quite mainstream tasks such as image segmentation and signal processing, for which CNNs perform well at.&lt;/p&gt;
&lt;p id=&quot;16cc&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;CNNs have been used for understanding in Natural Language Processing (NLP) and speech recognition, although often for NLP Recurrent Neural Nets (RNNs) are used.&lt;/p&gt;
&lt;p id=&quot;ae6d&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;A CNN can also be implemented as a U-Net architecture, which are essentially two almost mirrored CNNs resulting in a CNN whose architecture can be presented in a U shape. U-nets are used where the output needs to be of similar size to the input such as segmentation and image improvement.&lt;/p&gt;
&lt;h2 id=&quot;16ad&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;Interesting uses for CNNs other than image processing&lt;/h2&gt;
&lt;p id=&quot;f6b1&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;More and more diverse and interesting uses are being found for CNN architectures. An example of a non-image based application is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.ncbi.nlm.nih.gov/pubmed/30517664&quot;&gt;&amp;ldquo;The Unreasonable Effectiveness of Convolutional Neural Networks in Population Genetic Inference&amp;rdquo;&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;by Lex Flagel et al. This is used to perform selective sweeps, finding gene flow, inferring population size changes, inferring rate of recombination.&lt;/p&gt;
&lt;p id=&quot;3873&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;There are researchers such as Professor Gerald Quon at the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://qlab.faculty.ucdavis.edu/publications/&quot;&gt;Quon-titative biology lab&lt;/a&gt;, using CNNs for generative models in single cell genomics for disease identification.&lt;/p&gt;
&lt;p id=&quot;8b3e&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;CNNs are also being used in astrophysics to interpret radio telescope data to predict the likely visual image to represent the data.&lt;/p&gt;
&lt;p id=&quot;3641&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://deepmind.com/blog/wavenet-generative-model-raw-audio/&quot;&gt;Deepmind&amp;rsquo;s WaveNet&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a CNN model for generating synthesized voice, used as the basis for Google&amp;rsquo;s Assistant&amp;rsquo;s voice synthesizer.&lt;/p&gt;
&lt;h1 id=&quot;f2d7&quot; data-selectable-paragraph=&quot;&quot;&gt;Convolutional kernels&lt;/h1&gt;
&lt;p id=&quot;1083&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Each convolutional layer contains a series of filters known as convolutional kernels. The filter is a matrix of integers that are used on a subset of the input pixel values, the same size as the kernel. Each pixel is multiplied by the corresponding value in the kernel, then the result is summed up for a single value for simplicity representing a grid cell, like a pixel, in the output channel/feature map.&lt;/p&gt;
&lt;p id=&quot;96cb&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;These are linear transformations, each convolution is a type of affine function.&lt;/p&gt;
&lt;p id=&quot;012e&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;In computer vision the input is often a 3 channel RGB image. For simplicity, if we take a greyscale image that has one channel (a two dimensional matrix) and a 3x3 convolutional kernel (a two dimensional matrix). The kernel strides over the input matrix of numbers moving horizontally column by column, sliding/scanning over the first rows in the matrix containing the images pixel values. Then the kernel strides down vertically to subsequent rows. Note, the filter may stride over one or several pixels at a time, this is detailed further below.&lt;/p&gt;
&lt;p id=&quot;8ff3&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;In other non-vision applications, a one dimensional convolution may slide vertically over an input matrix.&lt;/p&gt;
&lt;h1 id=&quot;dd40&quot; data-selectable-paragraph=&quot;&quot;&gt;Creating a feature map from a convolutional kernel&lt;/h1&gt;
&lt;p id=&quot;5328&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Below is a diagram showing the operation of the convolutional kernel.&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSyf4n/btrWYMqJclB/QTi1F8jE8VFFtcFhVdc5L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSyf4n/btrWYMqJclB/QTi1F8jE8VFFtcFhVdc5L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSyf4n/btrWYMqJclB/QTi1F8jE8VFFtcFhVdc5L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSyf4n%2FbtrWYMqJclB%2FQTi1F8jE8VFFtcFhVdc5L0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;357&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A stride one 3x3 convolutional kernel acting on a 8x8 input image, outputting an 8x8 filter/channel. Source:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.researchgate.net/figure/a-Illustration-of-the-operation-principle-of-the-convolution-kernel-convolutional-layer_fig2_309487032&quot;&gt;https://www.researchgate.net/figure/a-Illustration-of-the-operation-principle-of-the-convolution-kernel-convolutional-layer_fig2_309487032&lt;/a&gt;&lt;/p&gt;
&lt;p id=&quot;a342&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Below is a visualisation from an excellent presentation, showing the kernel scanning over the values in the input matrix.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzcoNB/btrWZVVlgfH/LSH8stDYcffSXH7sba50WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzcoNB/btrWZVVlgfH/LSH8stDYcffSXH7sba50WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzcoNB/btrWZVVlgfH/LSH8stDYcffSXH7sba50WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzcoNB%2FbtrWZVVlgfH%2FLSH8stDYcffSXH7sba50WK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;691&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kernel scanning over the values in the input matrix. Source: Otavio Good: excerpt&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=f0t-OCG79-U&quot;&gt;https://www.youtube.com/watch?v=f0t-OCG79-U&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Oqm9vsf_hvU&quot;&gt;https://www.youtube.com/watch?v=Oqm9vsf_hvU&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;9e80&quot; data-selectable-paragraph=&quot;&quot;&gt;Padding&lt;/h1&gt;
&lt;p id=&quot;b3f6&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;To handle the edge pixels there are several approaches:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;9076&quot; data-selectable-paragraph=&quot;&quot;&gt;Losing the edge pixels&lt;/li&gt;
&lt;li id=&quot;925e&quot; data-selectable-paragraph=&quot;&quot;&gt;Padding with zero value pixels&lt;/li&gt;
&lt;li id=&quot;596b&quot; data-selectable-paragraph=&quot;&quot;&gt;Reflection padding&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;561e&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Reflection padding is by far the best approach, where the number of pixels needed for the convolutional kernel to process the edge pixels are added onto the outside copying the pixels from the edge of the image. For a 3x3 kernel, one pixel needs to be added around the outside, for a 7x7 kernel then three pixels would be reflected around the outside. The pixels added around each side is the dimension, halved and rounded down.&lt;/p&gt;
&lt;p id=&quot;95db&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Traditionally in many research papers, the edge pixels are just ignored, which loses a small proportion of the data and this gets increasing worse if there are many deep convolutional layers. For this reason, I could not find existing diagrams to easily convey some of the points here without being misleading and confusing stride 1 convolutions with stride 2 convolutions.&lt;/p&gt;
&lt;p id=&quot;9b6b&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;With padding, the output from a input of width w and height h would be width w and height h (the same as the input with a single input channel), assuming the kernel takes a stride of one pixel at a time.&lt;/p&gt;
&lt;h1 id=&quot;92ef&quot; data-selectable-paragraph=&quot;&quot;&gt;Creating multiple channels/feature maps with multiple kernels&lt;/h1&gt;
&lt;p id=&quot;8f00&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;When multiple convolutional kernels are applied within a convolutional layer, many channels/feature maps are created, one from each convolutional kernel. Below is a visualisation below showing the channels/feature maps being created.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1083&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co0eRQ/btrWYMxvzA4/Yz8aLt6O1SyddUzINJw9Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co0eRQ/btrWYMxvzA4/Yz8aLt6O1SyddUzINJw9Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co0eRQ/btrWYMxvzA4/Yz8aLt6O1SyddUzINJw9Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco0eRQ%2FbtrWYMxvzA4%2FYz8aLt6O1SyddUzINJw9Hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;542&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1083&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visualisation of channels/feature maps created from a layer of convolutional kernels. Source: Otavio Good: excerpt&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=f0t-OCG79-U&quot;&gt;https://www.youtube.com/watch?v=f0t-OCG79-U&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Oqm9vsf_hvU&quot;&gt;https://www.youtube.com/watch?v=Oqm9vsf_hvU&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;a000&quot; data-selectable-paragraph=&quot;&quot;&gt;RGB 3 channel input&lt;/h1&gt;
&lt;p id=&quot;b624&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Most image processing needs to operate on RGB images with three channels. A RGB image is a three dimensional array of numbers otherwise known as a rank three tensor.&lt;/p&gt;
&lt;p id=&quot;86a5&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;When processing a three channel RGB image, a convolutional kernel that is a three dimensional array/rank 3 tensor of numbers would normally be used. It is very common for the convolutional kernel to be of size 3x3x3 &amp;mdash; the convolutional kernel being like a cube.&lt;/p&gt;
&lt;p id=&quot;a463&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Usually there is at least three convolutional kernels in order that each can act as a different filter to gain insight from each colour channel.&lt;/p&gt;
&lt;p id=&quot;cfea&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;The convolution kernels as a group make a four dimensional array, otherwise known as a rank four tensor. It is difficult, if not impossible, to visualise dimensions when they are higher than three. In this case imagine it as a list of three dimensional cubes.&lt;/p&gt;
&lt;p id=&quot;3277&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;The filter moves across the input data in the same way, sliding or taking strides across the rows then moving down the columns and striding across the rows until it reaches the bottom right corner:&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Of4Dr/btrW4FdirNx/jBFhI9FsTR7kWM4X14ETpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Of4Dr/btrW4FdirNx/jBFhI9FsTR7kWM4X14ETpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Of4Dr/btrW4FdirNx/jBFhI9FsTR7kWM4X14ETpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOf4Dr%2FbtrW4FdirNx%2FjBFhI9FsTR7kWM4X14ETpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;326&quot; height=&quot;463&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3x3x3 convolutional kernel acting on a 3 channel input. Source:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://machinethink.net/images/vggnet-convolutional-neural-network-iphone/ConvolutionKernel@2x.png&quot;&gt;https://machinethink.net/images/vggnet-convolutional-neural-network-iphone/ConvolutionKernel@2x.png&lt;/a&gt;&lt;/p&gt;
&lt;p id=&quot;0ff0&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;With padding and a stride of one, the output from an input of width x, height y and depth 3 would be width x, height y and depth 1, as the cube produces a single summed output value from each stride. For example, with an input of 3x64x64 (say a 64x64 RGB three channel image) then one kernel taking strides of one with padding the edge pixels would output a channel/feature map of 64x64 (one channel).&lt;/p&gt;
&lt;p id=&quot;d035&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;It is worth noting the input is often normalised, this is detailed further below.&lt;/p&gt;
&lt;h1 id=&quot;1d35&quot; data-selectable-paragraph=&quot;&quot;&gt;Strides&lt;/h1&gt;
&lt;p id=&quot;d1b7&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;It is common to use a stride two convolution rather than a stride one convolution, where the convolutional kernel strides over 2 pixels at a time, for example our 3x3 kernel would start at position (1,1), then stride to (1,3), then to 1, 5) and so on, halving the size of the output channel/feature map, compared to the convolutional kernel taking strides of one.&lt;/p&gt;
&lt;p id=&quot;acf1&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;With padding, the output from an input of width w, height h and depth 3 would be the ceiling of width w/2, height h/2 and depth 1, as the kernel outputs a single summed output from each stride.&lt;/p&gt;
&lt;p id=&quot;b566&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;For example, with an input of 3x64x64 (say a 64x64 RGB three channel image), one kernel taking strides of two with padding the edge pixels, would produce a channel/feature map of 32x32.&lt;/p&gt;
&lt;h1 id=&quot;4edb&quot; data-selectable-paragraph=&quot;&quot;&gt;Many kernels&lt;/h1&gt;
&lt;p id=&quot;a208&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;In CNN models there are often there are many more than three convolutional kernels, 16 kernels or even 64 kernels in a convolutional layer is common.&lt;/p&gt;
&lt;p id=&quot;1a7a&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;These different convolution kernels each act as a different filter creating a channel/feature map representing something different. For example, kernels could be filtering top edges, bottom edges, diagonal lines and so on. In much deeper networks these kernels could be filtering to animal features such as eyes or bird wings.&lt;/p&gt;
&lt;p id=&quot;0c6c&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Having a higher number of convolutional kernels creates a higher number of channels/feature maps and a growing amount of data and this uses more memory. The stride 2 convolution, as per the above example, helps to reduce the memory usage as the output channel of the stride 2 convolution has half the width and height of the input. This assumes reflection padding is being used otherwise it could be slightly smaller.&lt;/p&gt;
&lt;h2 id=&quot;cf6b&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;An example of several convolutional layers of stride 2&lt;/h2&gt;
&lt;p id=&quot;da16&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;With a 64 pixel square input with three channels and 16 3x3x3 kernels our convolutional layer would have:&lt;/p&gt;
&lt;p id=&quot;daa2&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Input: 64x64x3&lt;br /&gt;Convolutional kernels: 16x3x3x3 (a four dimensional tensor)&lt;br /&gt;Output/activations of the convolutional kernels: 16x32x32 (16 channels/feature maps of 32x32)&lt;/p&gt;
&lt;p id=&quot;cb2b&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;The network could then apply batch normalisation to decrease learning time and reduce overfitting, more details below. In addition a non-linear activation function, such as RELU is usually applied to allow the network to approximate better, more details below.&lt;/p&gt;
&lt;p id=&quot;e7fe&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Often there are several layers of stride 2 convolutions, creating an increasing number of channels/feature maps. Taking the example above a layer deeper:&lt;/p&gt;
&lt;p id=&quot;2b25&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Input: 16x32x32&lt;br /&gt;Convolutional kernels: 64x3x3x3&lt;br /&gt;Output/activations of the convolutional kernels: 64x16x16 (64 channels/feature maps of 16x16)&lt;/p&gt;
&lt;p id=&quot;2599&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Then after applying ReLU and batch normalisation (see below), another stride 2 convolution is applied:&lt;/p&gt;
&lt;p id=&quot;465b&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Input: 64x16x16&lt;br /&gt;Convolutional kernels: 128x3x3x3&lt;br /&gt;Output/activations of the convolutional kernels: 128x8x8 (128 channels/feature maps of 8x8).&lt;/p&gt;
&lt;h1 id=&quot;dd55&quot; data-selectable-paragraph=&quot;&quot;&gt;Classification&lt;/h1&gt;
&lt;p id=&quot;030d&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;If, for example, an image belongs to one of 42 categories and the network&amp;rsquo;s goal is to predict which category the image belongs to.&lt;/p&gt;
&lt;p id=&quot;aae1&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Following on from the above example with an output of 128x8x8, first the average pool of the rank 3 tensor is taken. The average pool is the mean average of each channel, in the this example each 8x8 matrix is averaged into a single number, with 128 channels/feature maps. This creates 128 numbers, a vector of size 1x128.&lt;/p&gt;
&lt;p id=&quot;fe3f&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;The next layer is a matrix or rank 2 tensor of 128x42 weights. The input 1x128 matrix is (dot product) multiplied by the 128x42 matrix producing a 1x42 vector. How activated each of the 42 grid cells/vector elements are, is how much the prediction matches that classification represented by that vector element. Softmax is applied as an activation function and then argmax to select the element highest value.&lt;/p&gt;
&lt;h1 id=&quot;1a00&quot; data-selectable-paragraph=&quot;&quot;&gt;Rectified Linear Unit (ReLU)&lt;/h1&gt;
&lt;p id=&quot;0068&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;A Rectified Linear Unit is used as a non-linear activation function. A ReLU says if the value is less than zero, round it up to zero.&lt;/p&gt;
&lt;h1 id=&quot;6fea&quot; data-selectable-paragraph=&quot;&quot;&gt;Normalisation&lt;/h1&gt;
&lt;p id=&quot;6000&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Normalisation is the process of subtracting the mean and dividing by the standard deviation. It transforms the range of the data to be between -1 and 1 making the data use the same scale, sometimes called Min-Max scaling.&lt;/p&gt;
&lt;p id=&quot;01ab&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;It is common to normalize the input features, standardising the data by removing the mean and scaling to unit variance. It is often important the input features are centred around zero and have variance in the same order.&lt;/p&gt;
&lt;p id=&quot;3ed8&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;With some data, such as images the data is scaled so that it&amp;rsquo;s range is between 0 and 1, most simply dividing the pixel values by 255.&lt;/p&gt;
&lt;p id=&quot;c433&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;This also allows the training process to find the optimal parameters quicker.&lt;/p&gt;
&lt;h1 id=&quot;ba3f&quot; data-selectable-paragraph=&quot;&quot;&gt;Batch normalisation&lt;/h1&gt;
&lt;p id=&quot;e7d5&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Batch normalisation has the benefits of helping to make a network output more stable predictions, reduce overfitting through regularisation and speeds up training by an order of magnitude.&lt;/p&gt;
&lt;p id=&quot;e53e&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Batch normalisation is the process of carrying normalisation within the scope activation layer of the current batch, subtracting the mean of the batch&amp;rsquo;s activations and dividing by the standard deviation of the batches activations.&lt;/p&gt;
&lt;p id=&quot;c975&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;This is necessary as even after normalizing the input as some activations can be higher, which can cause the subsequent layers to act abnormally and makes the network less stable.&lt;/p&gt;
&lt;p id=&quot;36c7&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;As batch normalisation has scaled and shifted the activation outputs, the weights in the next layer will no longer be optimal. Stochastic gradient descent (SGD) would undo the normalisation, as it would minimise the loss function.&lt;/p&gt;
&lt;p id=&quot;7cb5&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;To prevent this effect two trainable parameters can be added to each layer to allow SGD to denormalise the output. These parameters are a mean parameter &amp;ldquo;beta&amp;rdquo; and a standard deviation parameter &amp;ldquo;gamma&amp;rdquo;. Batch normalisation sets these two weights for each activation output to allow the normalisation to be reversed to get the raw input, this avoids affecting the stability of the network by avoiding having to update the other weights.&lt;/p&gt;
&lt;h1 id=&quot;9c88&quot; data-selectable-paragraph=&quot;&quot;&gt;Why CNNs are so powerful&lt;/h1&gt;
&lt;p id=&quot;4b29&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;In simple terms a large enough CNN can solve any solvable problem.&lt;/p&gt;
&lt;p id=&quot;efb1&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Notable CNN architecture&amp;rsquo;s that perform exceptionally well across many different image processing tasks are the VGG models ( K. Simonyan and A. Zisserman), the ResNet models (Kaiming He et al) and the Google Inception models (Christian Szegedy et al). These models have millions of trainable parameters.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3F2qb/btrWRXAwnFN/ZIS52ByJRhrD3ZTPOSCX70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3F2qb/btrWRXAwnFN/ZIS52ByJRhrD3ZTPOSCX70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3F2qb/btrWRXAwnFN/ZIS52ByJRhrD3ZTPOSCX70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3F2qb%2FbtrWRXAwnFN%2FZIS52ByJRhrD3ZTPOSCX70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;395&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VGG-16 Network Architecture. Source:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://neurohive.io/wp-content/uploads/2018/11/vgg16-1-e1542731207177.png&quot;&gt;https://neurohive.io/wp-content/uploads/2018/11/vgg16-1-e1542731207177.png&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;0dc9&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;Universal approximation theorem&lt;/h2&gt;
&lt;p id=&quot;7810&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;The Universal approximation theorem essentially states if a problem can be solved it can be solved by deep neural networks, given enough layers of affine functions layered with non-linear functions. Essentially a stack of linear functions followed by non-linear functions could solve any problem that is solvable.&lt;/p&gt;
&lt;p id=&quot;fbe3&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Practically in implementation this can be many matrix multiplication with large enough matrices followed by RELU, stacked together these have a mathematical property resulting in being able to solve any arbitrary complex mathematical function to any arbitrary high level of accuracy assuming you have the time and resource to train it.&lt;/p&gt;
&lt;p id=&quot;f5b8&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Whether this would give the neural network understanding is a debated topic, especially by cognitive scientists. The argument is that no matter how well you approximate the syntax and semantics of a problem, you never understand it. This is basically the foundation of Searle&amp;rsquo;s Chinese Room Argument. Some would argue that does it matter if you can approximate the solution to the problem well enough that it&amp;rsquo;s indistinguishable from understanding the problem.&lt;/p&gt;
&lt;h1 id=&quot;660e&quot; data-selectable-paragraph=&quot;&quot;&gt;Fastai courses&lt;/h1&gt;
&lt;p id=&quot;bbee&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;I would like to thank the Fastai team whose courses have helped cement my deep learning and CNN knowledge providing an excellent starting point for further learning and understanding.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;from &lt;a href=&quot;https://towardsdatascience.com/an-introduction-to-convolutional-neural-networks-eb0b60b58fd7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://towardsdatascience.com/an-introduction-to-convolutional-neural-networks-eb0b60b58fd7&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/373</guid>
      <comments>https://brightlightkim.tistory.com/373#entry373comment</comments>
      <pubDate>Tue, 24 Jan 2023 14:45:24 +0900</pubDate>
    </item>
    <item>
      <title>Tailwind CSS Intro</title>
      <link>https://brightlightkim.tistory.com/372</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind CSS is a utility-first&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/wordpress-css/&quot;&gt;CSS (Cascading Style Sheets)&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;framework with predefined classes that you can use to build and design web pages directly in your markup. It lets you write CSS in your HTML in the form of predefined classes.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;ll define what a framework is and what we mean by &amp;ldquo;utility-first CSS&amp;rdquo; to help you better understand what Tailwind CSS is all about.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-framework&quot; data-ke-size=&quot;size26&quot;&gt;What Is a Framework?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As a general programming term,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/javascript-libraries/#what-are-javascript-frameworks&quot;&gt;a framework&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a tool that provides reusable and ready-made components built from the features of an already existing tool. The overall purpose of creating frameworks is to increase development speed by doing less work.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Now that we&amp;rsquo;ve established the meaning of a framework, it should help you understand that Tailwind CSS is a tool built upon CSS features. All the framework&amp;rsquo;s functionalities were derived from CSS styles composed as classes.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-utilityfirst-css-framework&quot; data-ke-size=&quot;size26&quot;&gt;What Is a Utility-First CSS Framework?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When we say utility-first CSS, we refer to classes&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/html-best-practices/&quot;&gt;in our markup (HTML)&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;with predefined functionalities. This implies that you only have to write a class with predefined styles attached to it, and those styles will be applied to the element.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In a case where you are working with vanilla CSS (CSS without any framework or library), you would first give your element a class name and then attach different properties and values to that class, which will, in turn, apply styling to your element.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;ll show you an example. Here, we&amp;rsquo;ll create a button with rounded corners and a text that says &amp;ldquo;Click me.&amp;rdquo; This is what the button will look like:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;77&quot; data-origin-height=&quot;40&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bonCeN/btrSWxLTfDV/u6H79SkkfkzTF5xJCSuK4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bonCeN/btrSWxLTfDV/u6H79SkkfkzTF5xJCSuK4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bonCeN/btrSWxLTfDV/u6H79SkkfkzTF5xJCSuK4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbonCeN%2FbtrSWxLTfDV%2Fu6H79SkkfkzTF5xJCSuK4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;100&quot; height=&quot;52&quot; data-origin-width=&quot;77&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Our button.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;ll first do this using vanilla CSS, and then using utility classes available in Tailwind CSS.&lt;/p&gt;
&lt;h3 id=&quot;with-vanilla-css&quot; data-ke-size=&quot;size23&quot;&gt;With Vanilla CSS&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;btn&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;ve given button tags the class&lt;span&gt;&amp;nbsp;&lt;/span&gt;btn, which will be styled using an external stylesheet. That is:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.btn {
  background-color: #000;
  color: #fff;
  padding: 8px;
  border: none;
  border-radius: 4px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;with-tailwind-css&quot; data-ke-size=&quot;size23&quot;&gt;With Tailwind CSS&lt;/h3&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;bg-black p-2 rounded&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is all required to achieve the same effect as the example with vanilla CSS. No external stylesheet where you have to write the styles was created because each class name we used already has a predefined style.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites-for-using-tailwind-css&quot; data-ke-size=&quot;size26&quot;&gt;Prerequisites for Using Tailwind CSS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before using Tailwind CSS, there are some prerequisites that you should consider meeting to use the framework&amp;rsquo;s features without difficulties. Here are a few of them:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kinsta.com/blog/html-vs-html5/&quot;&gt;Good knowledge of HTML&lt;/a&gt;, its structure, and how it works&lt;/li&gt;
&lt;li&gt;Solid foundation in CSS &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/optimize-css/#use-media-query-rendering&quot;&gt;media queries&lt;/a&gt;, flexbox, and grid system&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;where-can-tailwind-css-be-used&quot; data-ke-size=&quot;size23&quot;&gt;Where Can Tailwind CSS Be Used?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You can use Tailwind CSS in frontend web projects, including JavaScript frameworks like&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/javascript-libraries/#reactjs&quot;&gt;React.js&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/knowledgebase/next-js/&quot;&gt;Next.js&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/knowledgebase/what-is-laravel/&quot;&gt;Laravel&lt;/a&gt;, Vite, Gatsby, etc.&lt;/p&gt;
&lt;h3 id=&quot;pros-and-cons-of-tailwind-css&quot; data-ke-size=&quot;size23&quot;&gt;Pros and Cons of Tailwind CSS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here are some of the advantages of using Tailwind CSS:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Faster development process&lt;/li&gt;
&lt;li&gt;Helps you practice your CSS more as the utilities are similar&lt;/li&gt;
&lt;li&gt;All utilities and components are easily customizable&lt;/li&gt;
&lt;li&gt;The overall file size for production is usually small&lt;/li&gt;
&lt;li&gt;Easy to learn if you already know CSS&lt;/li&gt;
&lt;li&gt;Good documentation for learning&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Some of the disadvantages of using Tailwind CSS include:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Your markup might look disorganized for large projects because all the styles are in the HTML files.&lt;/li&gt;
&lt;li&gt;It isn&amp;rsquo;t easy to learn if you don&amp;rsquo;t understand CSS well.&lt;/li&gt;
&lt;li&gt;You are forced to build everything from scratch, including your input elements. When you install Tailwind CSS, it removes all default CSS styles.&lt;/li&gt;
&lt;li&gt;Tailwind CSS is not the best option if you are looking to minimize time spent&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/how-to-become-a-web-developer/&quot;&gt;developing&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;your website&amp;rsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/frontend-developer/&quot;&gt;frontend&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and mainly focusing on the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/backend-developer/&quot;&gt;backend&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;logic.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;when-to-use-tailwind-css&quot; data-ke-size=&quot;size23&quot;&gt;When to Use Tailwind CSS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind CSS is best used to speed up the development process by writing less code. It comes with a design system that helps maintain consistency across various design requirements like padding, spacing, and so forth; with this, you do not have to worry about creating your design systems.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You can also use Tailwind CSS if you are looking to use a framework that is easily configurable because it does not force you to use components (navigation bars, buttons,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/wordpress-forms/&quot;&gt;forms&lt;/a&gt;, and so forth) in the same way all the time; you get to choose what your components should look like. But you should never use Tailwind if you have not learned and practiced CSS.&lt;/p&gt;
&lt;h3 id=&quot;similarities-and-differences-between-tailwind-css-and-other-css-frameworks&quot; data-ke-size=&quot;size23&quot;&gt;Similarities and Differences Between Tailwind CSS and Other CSS Frameworks&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here are a few similarities:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;They help you get work done faster.&lt;/li&gt;
&lt;li&gt;They come with predefined classes.&lt;/li&gt;
&lt;li&gt;They were built upon CSS rules.&lt;/li&gt;
&lt;li&gt;They&amp;rsquo;re both easy to learn and use with a working knowledge of CSS.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Now let&amp;rsquo;s look at some of the differences:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tailwind is different from most frameworks because you have to create your components. For example, Bootstrap has components like navigation bars, buttons, and so forth, but with Tailwind, you have to build all that yourself.&lt;/li&gt;
&lt;li&gt;Some frameworks like&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/responsive-web-design/#bootstraps-responsive-breakpoints&quot;&gt;Bootstrap&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;are not easily customizable, so you are forced to use their design patterns. In Tailwind, you control the flow of everything.&lt;/li&gt;
&lt;li&gt;In-depth knowledge of CSS is required to use Tailwind. Writing class names is not enough, since you must combine them as though you were writing vanilla CSS to achieve the same result. All you need to know in most other frameworks is what component will be built out when you use a class name.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;how-to-get-started-with-using-tailwind-css&quot; data-ke-size=&quot;size26&quot;&gt;How to Get Started With Using Tailwind CSS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before installing Tailwind CSS and integrating it in your project, make sure that:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;You have&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/how-to-install-node-js/&quot;&gt;Node.js installed on your computer&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to make use of the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/knowledgebase/what-is-node-js/#what-is-npm&quot;&gt;Node package manager (npm)&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in the terminal.&lt;/li&gt;
&lt;li&gt;Your project is all set up with your files created.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is what our project structure looks like at the moment:&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;-Tailwind-tutorial
    -public
        -index.html
        -styles.css
    -src
        -styles.css&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, start up a terminal for your project and run the following commands:&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;    npm install -D tailwindcss&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The above command will install the Tailwind CSS framework as a dependency. Next, generate your tailwind.config.js file by running the command below:&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;    npm install -D tailwindcss&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The tailwind.config.js file will be empty when created, so we&amp;rsquo;ve to add some lines of code:&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;module.exports = {
  content: [&quot;./src/**/*.{html,js}&quot;, &quot;./public/*.html&quot;],
  theme: {
    extend: {},
  },
  plugins: [],
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The file paths provided in the content array will enable Tailwind to purge/remove any unused styles during build time.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Want to know how we increased our traffic over 1000%?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Join 20,000+ others who get our weekly newsletter with insider WordPress tips!&lt;/p&gt;
&lt;/div&gt;
&lt;a href=&quot;https://kinsta.com/blog/tailwind-css/#newsletter&quot; data-dialog-src=&quot;#newsletter&quot;&gt;Subscribe Now&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The next thing to do is to add the &amp;ldquo;@tailwind&amp;rdquo; directives to your CSS file in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;src&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;folder &amp;mdash; this is where Tailwind generates all of its predefined utility styles for you:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;@tailwind base;
@tailwind components;
@tailwind utilities;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The last thing to do is to start the build process by running this command in your terminal:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;    npx tailwindcss -i ./src/styles.css -o ./public/styles.css --watch&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the code above, we&amp;rsquo;re telling Tailwind that our input file is the stylesheet in the src folder and that whatever styles we&amp;rsquo;ve used have to be built into the output file in the public folder.&lt;span&gt;&amp;nbsp;&lt;/span&gt;--watch&lt;span&gt;&amp;nbsp;&lt;/span&gt;allows Tailwind to watch your file for changes for an automatic build process; omitting it means we&amp;rsquo;ve to run that script every time we want to build our code and see the desired output.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://twitter.com/intent/tweet?url=https%3A%2F%2Fkinsta.com%2Fblog%2Ftailwind-css%2F&amp;amp;via=kinsta&amp;amp;text=Want+to+do+more+while+writing+less+code%3F+%F0%9F%91%80+A+solid+frontend+framework+is+one+way+to+accomplish+that.+Start+with+Tailwind+CSS+%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB&amp;amp;hashtags=CSS%2CWebDev&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Want to do more while writing less code?   A solid frontend framework is one way to accomplish that. Start with Tailwind CSS  &amp;zwj; &lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #999999;&quot;&gt;&lt;span&gt;CLICK TO TWEET&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;using-tailwind-css&quot; data-ke-size=&quot;size23&quot;&gt;Using Tailwind CSS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Now that we&amp;rsquo;ve installed and set up Tailwind CSS for our project, let&amp;rsquo;s see some examples to understand its application fully.&lt;/p&gt;
&lt;h4 id=&quot;flexbox-example&quot; data-ke-size=&quot;size20&quot;&gt;Flexbox Example&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To use&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/responsive-web-design/&quot;&gt;flex&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in Tailwind CSS, you need to add a class of flex and then the direction of the flex items:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;    &amp;lt;div class=&quot;flex flex-row&quot;&amp;gt;
      &amp;lt;button&amp;gt; Button 1 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 2 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 3 &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOq7vU/btrSYCTvQvU/iGwLphPRNNeyMHZM0TEL1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOq7vU/btrSYCTvQvU/iGwLphPRNNeyMHZM0TEL1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOq7vU/btrSYCTvQvU/iGwLphPRNNeyMHZM0TEL1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOq7vU%2FbtrSYCTvQvU%2FiGwLphPRNNeyMHZM0TEL1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;582&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Our three purple buttons.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Using&lt;span&gt;&amp;nbsp;&lt;/span&gt;flex-row-reverse&lt;span&gt;&amp;nbsp;&lt;/span&gt;will reverse the order in which the buttons appear.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flex-col&lt;span&gt;&amp;nbsp;&lt;/span&gt;stacks them above each other. Here is an example:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;    &amp;lt;div class=&quot;flex flex-col&quot;&amp;gt;
      &amp;lt;button&amp;gt; Button 1 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 2 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 3 &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n6OtB/btrS0ghzBLZ/kIzNkGpUusY5aZoxwJX9x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n6OtB/btrS0ghzBLZ/kIzNkGpUusY5aZoxwJX9x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n6OtB/btrS0ghzBLZ/kIzNkGpUusY5aZoxwJX9x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn6OtB%2FbtrS0ghzBLZ%2FkIzNkGpUusY5aZoxwJX9x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;582&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Our three purple buttons.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Just like the previous example,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;flex-col-reverse&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;reverses the order.&lt;/p&gt;
&lt;h4 id=&quot;grid-example&quot; data-ke-size=&quot;size20&quot;&gt;Grid Example&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When specifying columns and rows in the grid system, we take a different approach by passing in a number that will determine how the elements will occupy available space:&lt;/p&gt;
&lt;div&gt;
&lt;div id=&quot;simple-promo&quot;&gt;
&lt;div&gt;Struggling with downtime and WordPress problems? Kinsta is the hosting solution designed to save you time!&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/features/&quot;&gt;Check out our features&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;grid grid-cols-3&quot;&amp;gt;
      &amp;lt;button&amp;gt; Button 1 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 2 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 3 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 4 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 5 &amp;lt;/button&amp;gt;
      &amp;lt;button&amp;gt; Button 6 &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IF2ms/btrS0FVKh8i/trgeKoVPo2V9zdeZ5R4Ts1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IF2ms/btrS0FVKh8i/trgeKoVPo2V9zdeZ5R4Ts1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IF2ms/btrS0FVKh8i/trgeKoVPo2V9zdeZ5R4Ts1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIF2ms%2FbtrS0FVKh8i%2FtrgeKoVPo2V9zdeZ5R4Ts1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;582&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Our six purple buttons.&lt;/p&gt;
&lt;h4 id=&quot;colors&quot; data-ke-size=&quot;size20&quot;&gt;Colors&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind comes with a lot of predefined colors. Each color has a set of different variations, with the lightest variation being 50 and the darkest being 900.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here is a picture of multiple colors and their&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/html-font-color/&quot;&gt;HTML hex codes&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to illustrate this&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bquYz6/btrSWaQIYxU/L6R6KL1pFr9GGK6QiRW4K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bquYz6/btrSWaQIYxU/L6R6KL1pFr9GGK6QiRW4K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bquYz6/btrSWaQIYxU/L6R6KL1pFr9GGK6QiRW4K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbquYz6%2FbtrSWaQIYxU%2FL6R6KL1pFr9GGK6QiRW4K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;617&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Customizing colors from Tailwind&amp;rsquo;s default palette. (&lt;a href=&quot;https://tailwindcss.com/docs/customizing-colors#default-color-palette&quot;&gt;Image source)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;ll give an example of how you can do this using the red color above to give a button element a background color:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;bg-red-50&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-100&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-200&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-300&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-400&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-500&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-600&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-700&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-800&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;bg-red-900&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This syntax is the same for all colors in Tailwind except for black and white, which are written the same way but without the use of numbers:&lt;span&gt;&amp;nbsp;&lt;/span&gt;bg-black&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;bg-white.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To add text color, you use the class&lt;span&gt;&amp;nbsp;&lt;/span&gt;text-{color}:&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;p class=&quot;text-yellow-600&quot;&amp;gt;Hello World&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;padding&quot; data-ke-size=&quot;size20&quot;&gt;Padding&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind already has a design system that would help you keep a consistent scale across your designs. All you have to know is the syntax for applying each utility.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The following are utilities for adding padding to your elements:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;p&lt;span&gt;&amp;nbsp;&lt;/span&gt;denotes padding across the whole element.&lt;/li&gt;
&lt;li&gt;py&lt;span&gt;&amp;nbsp;&lt;/span&gt;denotes padding padding-top and padding-bottom.&lt;/li&gt;
&lt;li&gt;px&lt;span&gt;&amp;nbsp;&lt;/span&gt;denotes padding-left and padding-right.&lt;/li&gt;
&lt;li&gt;pt&lt;span&gt;&amp;nbsp;&lt;/span&gt;denotes padding-top.&lt;/li&gt;
&lt;li&gt;pr&lt;span&gt;&amp;nbsp;&lt;/span&gt;denotes padding-right.&lt;/li&gt;
&lt;li&gt;pb&lt;span&gt;&amp;nbsp;&lt;/span&gt;denotes padding-bottom.&lt;/li&gt;
&lt;li&gt;pl&lt;span&gt;&amp;nbsp;&lt;/span&gt;denotes padding-left&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To apply them to your elements, you&amp;rsquo;d have to use the appropriate numbers provided by Tailwind &amp;mdash; a bit similar to the numbers that represented color variants in the last section. Here&amp;rsquo;s what we mean:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;p-0&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;pt-1&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;pr-2&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;pb-3&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button class=&quot;pl-4&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;margin&quot; data-ke-size=&quot;size20&quot;&gt;Margin&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The predefined utilities for padding and margin are very similar. You have to replace the&lt;span&gt;&amp;nbsp;&lt;/span&gt;p&lt;span&gt;&amp;nbsp;&lt;/span&gt;with an&lt;span&gt;&amp;nbsp;&lt;/span&gt;m:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;m&lt;/li&gt;
&lt;li&gt;my&lt;/li&gt;
&lt;li&gt;mx&lt;/li&gt;
&lt;li&gt;mt&lt;/li&gt;
&lt;li&gt;mr&lt;/li&gt;
&lt;li&gt;mb&lt;/li&gt;
&lt;li&gt;ml&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-create-a-tailwind-css-plugin&quot; data-ke-size=&quot;size26&quot;&gt;How to Create a Tailwind CSS Plugin&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Even though Tailwind CSS has loads of utilities and design systems already built for you, it&amp;rsquo;s still possible that you may have a particular functionality that you would like to add to extend what Tailwind can be used for. Tailwind CSS allows us to do this by creating a plugin.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let&amp;rsquo;s get our hands dirty by creating a plugin that adds the aqua color and a rotate utility that rotates an element 150&amp;ordm; on the X-axis. We&amp;rsquo;ll make these utilities in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;tailwind.config.js&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file using a little bit of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/knowledgebase/what-is-javascript/&quot;&gt;JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const plugin = require(&quot;tailwindcss/plugin&quot;);

module.exports = {
  content: [&quot;./src/**/*.{html,js}&quot;, &quot;./public/*.html&quot;],
  theme: {
    extend: {},
  },
  plugins: [
    plugin(function ({ addUtilities }) {
      const myUtilities = {
        &quot;.bg-aqua&quot;: { background: &quot;aqua&quot; },
        &quot;.rotate-150deg&quot;: {
          transform: &quot;rotateX(150deg)&quot;,
        },
      };
      addUtilities(myUtilities);
    }),
  ],

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Now, let&amp;rsquo;s break this down. The first thing we did was to import Tailwind&amp;rsquo;s plugin function:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;const plugin = require(&quot;tailwindcss/plugin&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then we went on to create our plugins in the plugins array:&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;  plugins: [
    plugin(function ({ addUtilities }) {
      const newUtilities = {
        &quot;.bg-aqua&quot;: { background: &quot;aqua&quot; },
        &quot;.rotate-150deg&quot;: {
          transform: &quot;rotateX(150deg)&quot;,
        },
      };
      addUtilities(newUtilities);
    }),
  ],&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You might have to rerun the build script after making your plugin.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Now that the plugins have are ready, we can test them:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;bg-aqua rotate-150deg&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you did everything right, you should have a button with an aqua color with the text rotated to 150&amp;ordm; on the X-axis.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://twitter.com/intent/tweet?url=https%3A%2F%2Fkinsta.com%2Fblog%2Ftailwind-css%2F&amp;amp;via=kinsta&amp;amp;text=Your+secret+weapon+for+building+and+designing+web+pages%3F+%F0%9F%91%80+Tailwind+CSS+%F0%9F%98%8C&amp;amp;hashtags=CSS%2CWebDev&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Your secret weapon for building and designing web pages?   Tailwind CSS  &lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #999999;&quot;&gt;&lt;span&gt;CLICK TO TWEET&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; data-ke-size=&quot;size26&quot;&gt;Summary&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Frameworks are an invaluable option when it comes to speeding up your workflow. They help you build good-looking and professional web pages while maintaining consistency in design. Tailwind CSS provides many utility-forward CSS classes to help&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kinsta.com/blog/web-design-best-practices/&quot;&gt;take your web design and development to the next level&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This article taught us what Tailwind CSS is and what makes it a framework. We then looked at the installation process and saw a few examples that showed how we could create our custom plugins to extend the existing functionality.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you&amp;rsquo;ve followed up to this point, then you now have a basic but solid understanding of how Tailwind works. To get better at using such a utility-first framework, though, you must keep practicing and increase your knowledge of CSS if you don&amp;rsquo;t have a solid foundation already.&lt;/p&gt;</description>
      <category>Web/CSS</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/372</guid>
      <comments>https://brightlightkim.tistory.com/372#entry372comment</comments>
      <pubDate>Tue, 6 Dec 2022 16:01:11 +0900</pubDate>
    </item>
    <item>
      <title>Rust enums and pattern matching</title>
      <link>https://brightlightkim.tistory.com/371</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ViByl/btrSOxx8wle/yshDKbQr5qNdHZHmwAVV2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ViByl/btrSOxx8wle/yshDKbQr5qNdHZHmwAVV2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ViByl/btrSOxx8wle/yshDKbQr5qNdHZHmwAVV2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FViByl%2FbtrSOxx8wle%2FyshDKbQr5qNdHZHmwAVV2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;730&quot; height=&quot;487&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;587&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pattern matching and enums can be used for a number of things, like error handling, handling null values, and more. In this article, we will go over the basics of pattern matching, enums, and see how enums can be used with pattern matching, including:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/#pattern-matching-in-rust&quot;&gt;Pattern matching in Rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/#using-enums-in-rust&quot;&gt;Using enums in Rust&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/#adding-data-to-enum-variants&quot;&gt;Adding data to enum variants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/#result-and-option-enums&quot;&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enums&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/#result-enum&quot;&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/#option-enum&quot;&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But, to follow along with this article, you must have at least a basic understanding of the Rust programming language.&lt;/p&gt;
&lt;h2 id=&quot;pattern-matching-in-rust&quot; data-ke-size=&quot;size26&quot;&gt;Pattern matching in Rust&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pattern matching is a mechanism of programming languages that allows the flow of the program to branch into one of multiple branches on a given input.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let&amp;rsquo;s say we have a variable called&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that&amp;rsquo;s a string representing the name of a person. With each name, we display a fruit as shown below:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;John&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;papaya&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Annie&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;blueberry&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Michael&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;guava&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Gabrielle&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;apple&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Others&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;orange&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here, we created five branches; the name to the left side of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;represents a name pattern, and the fruit on the right side is the branch that will display. So if&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;John&lt;/span&gt;, we display a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;papaya&lt;/span&gt;, if&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Annie&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;we display&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;blueberry&lt;/span&gt;, and so on.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;But, if the value of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is not a registered pattern, it defaults to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Others&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In Rust, we perform pattern matching using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement, which we can use with our previous example in the following code:&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;match name {
  &quot;John&quot; =&amp;gt; println!(&quot;papaya&quot;),
  &quot;Annie&quot; =&amp;gt; println!(&quot;blueberry&quot;),
  &quot;Michael&quot; =&amp;gt; println!(&quot;guava&quot;),
  &quot;Gabrielle&quot; =&amp;gt; println!(&quot;apple&quot;),
  _ =&amp;gt; println!(&quot;orange&quot;),
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement in Rust works like the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement in other programming languages like C++, Java, JavaScript, or others. But, the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement has some advantages over the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For one, it checks if the variable&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;on the first line above matches any of the values at the left side of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and then executes what is on the right of the pattern that matches.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If all the patterns do not match the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variable, it defaults to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(which matches every value) and displays&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;orange&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in the terminal. This is just like the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Others&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;pattern we saw earlier.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Creating pattern matching like this is very powerful, but if we want to execute more than a line of code, we replace the right side of the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;block with a block of code that has all the lines you want to execute.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the following example, we modify the previous&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement to print a number and a color along with the fruit for each name:&lt;/p&gt;
&lt;pre class=&quot;golo&quot;&gt;&lt;code&gt;match name {
  &quot;John&quot; =&amp;gt; {
    println!(&quot;4&quot;);
    println!(&quot;green&quot;);
    println!(&quot;papaya&quot;);
  },
  &quot;Annie&quot; =&amp;gt; {
    println!(&quot;3&quot;);
    println!(&quot;blue&quot;);
    println!(&quot;blueberry&quot;);
  },
  &quot;Michael&quot; =&amp;gt; {
    println!(&quot;2&quot;);
    println!(&quot;yellow&quot;);
    println!(&quot;guava&quot;);
  },
  &quot;Gabrielle&quot; =&amp;gt; {
    println!(&quot;1&quot;);
    println!(&quot;purple&quot;);
    println!(&quot;apple&quot;);
  },
  _ =&amp;gt; {
    println!(&quot;0&quot;);
    println!(&quot;orange&quot;);
    println!(&quot;orange&quot;);
  },
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here, we converted the simple line:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;_ =&amp;gt; println!(&quot;orange&quot;),&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Into a statement that executes multiple lines of code on&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;_ =&amp;gt; {
    println!(&quot;0&quot;);
    println!(&quot;orange&quot;);
    println!(&quot;orange&quot;);
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With this, we can execute more than one line. And, if we want to return a value, we can either use the return statement or Rust&amp;rsquo;s return shortcut. The shortcut is done by removing the semicolon of the last expression:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;_ =&amp;gt; {
    println!(&quot;0&quot;);
    println!(&quot;orange&quot;);
    println!(&quot;orange&quot;);
    &quot;This is for the others&quot;
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Or by using the following:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;_ =&amp;gt; {
    println!(&quot;0&quot;);
    println!(&quot;orange&quot;);
    println!(&quot;orange&quot;);
    return &quot;This is for the others&quot;;
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A single line pattern also returns the value of the expression to the right of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;so you can do the following:&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;let result = match name {
  &quot;John&quot; =&amp;gt; &quot;papaya&quot;,
  &quot;Annie&quot; =&amp;gt; &quot;blueberry&quot;,
  &quot;Michael&quot; =&amp;gt; &quot;guava&quot;,
  &quot;Gabrielle&quot; =&amp;gt; &quot;apple&quot;,
  _ =&amp;gt; &quot;orange&quot;,
};

println!(&quot;{}&quot;, result);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This does the same thing as the first&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement example.&lt;/p&gt;
&lt;h2 id=&quot;using-enums-in-rust&quot; data-ke-size=&quot;size26&quot;&gt;Using enums in Rust&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enums are Rust data structures that represent a data type with more than one variant. Enums can perform the same operations that a struct can but use less memory and fewer lines of code.&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;div&gt;&lt;a href=&quot;https://lp.logrocket.com/blg/learn-more&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUq9sJ/btrSPPZJZKs/RSapNUvzjDKDbqHn7dk6q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUq9sJ/btrSPPZJZKs/RSapNUvzjDKDbqHn7dk6q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUq9sJ/btrSPPZJZKs/RSapNUvzjDKDbqHn7dk6q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUq9sJ%2FbtrSPPZJZKs%2FRSapNUvzjDKDbqHn7dk6q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;283&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Over 200k developers use LogRocket to create better digital experiences&lt;/h2&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;34&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nJKXY/btrSM4bTP8i/cfGKrAISKpC9RDoVCgWtZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nJKXY/btrSM4bTP8i/cfGKrAISKpC9RDoVCgWtZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nJKXY/btrSM4bTP8i/cfGKrAISKpC9RDoVCgWtZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnJKXY%2FbtrSM4bTP8i%2FcfGKrAISKpC9RDoVCgWtZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;34&quot; height=&quot;35&quot; data-origin-width=&quot;34&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
Learn more &amp;rarr;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can use any of an enum&amp;rsquo;s variants for our operations, but, we can only use the base enum for specifying that we will either return a variant from a function or assign it to a variable.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That means the base enum itself cannot be assigned to a variable. For an example of an enum, let&amp;rsquo;s create a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;vehicle&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum with three variants:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Car&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;MotorCycle&lt;/span&gt;, and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Bicycle&lt;/span&gt;.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum Vehicle {
  Car,
  MotorCycle,
  Bicycle,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can then access the variants by writing&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Vehicle&lt;/span&gt;&lt;span&gt;::&amp;lt;&lt;/span&gt;&lt;span&gt;variant&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;let vehicle = Vehicle::Car;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And, if you want to statically type it, you can write something like this:&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;let vehicle: Vehicle = Vehicle::Car;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;pattern-matching-with-enums&quot; data-ke-size=&quot;size23&quot;&gt;Pattern matching With Enums&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Just as we can perform pattern matching with strings, numbers, and other data types, we can also match enum variants too:&lt;/p&gt;
&lt;pre class=&quot;puppet&quot;&gt;&lt;code&gt;match vehicle {
    Vehicle::Car =&amp;gt; println!(&quot;I have four tires&quot;),
    Vehicle::MotorCycle =&amp;gt; println!(&quot;I have two tires and run on gas&quot;),
    Vehicle::Bicycle =&amp;gt; println!(&quot;I have two tires and run on your effort&quot;)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here, we:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Passed the variable into a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement&lt;/li&gt;
&lt;li&gt;Created patterns to match the individual variants&lt;/li&gt;
&lt;li&gt;Coded what will execute once the variable matches any pattern&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So, we can write a program like this:&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;enum Vehicle {
  Car,
  MotorCycle,
  Bicycle,
}

fn main() {
  let vehicle = Vehicle::Car;

  match vehicle {
    Vehicle::Car =&amp;gt; println!(&quot;I have four tires&quot;),
    Vehicle::MotorCycle =&amp;gt; println!(&quot;I have two tires and run on gas&quot;),
    Vehicle::Bicycle =&amp;gt; println!(&quot;I have two tires and run on your effort&quot;)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This results in the following:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&amp;gt; rustc example.rs
&amp;gt; ./example

I have four tires&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;adding-data-to-enum-variants&quot; data-ke-size=&quot;size23&quot;&gt;Adding data to enum variants&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We can also add data to our enum variants. We must specify that our variant can hold data by adding a parenthesis with the data types of what it will hold:&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;enum Vehicle {
  Car(String),
  MotorCycle(String),
  Bicycle(String),
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then, we can use it like this:&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;fn main() {
  let vehicle = Vehicle::Car(&quot;red&quot;.to_string());

  match vehicle {
    Vehicle::Car(color) =&amp;gt; println!(&quot;I am {} and have four tires&quot;, color),
    Vehicle::MotorCycle(color) =&amp;gt; println!(&quot;I am {} and have two tires and run on gas&quot;, color),
    Vehicle::Bicycle(color) =&amp;gt; println!(&quot;I am {} and have two tires and run on your effort&quot;, color)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here, we:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Created a new instance of the enum and assigned it to a variable&lt;/li&gt;
&lt;li&gt;Put that variable in a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement&lt;/li&gt;
&lt;li&gt;Destructured the contents of each enum variant into a variable within the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement&lt;/li&gt;
&lt;li&gt;Used the destructed variable in the codeblock by the right of the pattern.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;result-and-option-enums&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enums&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enums are part of the standard libraries used in Rust for handling results, errors, and null values of a function or a variable.&lt;/p&gt;
&lt;h3 id=&quot;result-enum&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is a very common enum in Rust that handles errors from a function or variable. It has two variants:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Ok&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Err&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Ok&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant holds the data returned if there are no errors, and the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant holds the error message. For example, we can create a function that returns a variant of the enum:&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;fn divide(numerator: i32, denominator: i32) -&amp;gt; Result&amp;lt;i32, String&amp;gt; {
    if denominator == 0 {
        return Err(&quot;Cannot divide by zero&quot;.to_string());
    } else {
        return Ok(numerator / denominator);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This function takes two arguments. The first one is the numerator and the second is the denominator. It returns the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant if the denominator is zero and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Ok&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;with the result if the denominator isn&amp;rsquo;t zero.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And then, we can use the function with pattern matching:&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;fn main() {
    match divide(103, 2) {
        Ok(solution) =&amp;gt; println!(&quot;The answer is {}&quot;, solution),
        Err(error) =&amp;gt; println!(&quot;Error: {}&quot;, error)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this example;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;We attempt to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;divide&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;103&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Create the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Ok&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;pattern match and extract the data it contains.&lt;/li&gt;
&lt;li&gt;Underneath that, create an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;pattern that gets any error message&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum also allows us to handle the errors without using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement. That means we can use any or all of the following:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Unwrap&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;gets the data in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Ok&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Unwrap_err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;obtains the error message from the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&amp;lsquo;s&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant&lt;/li&gt;
&lt;li&gt;&lt;span&gt;is_err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;returns true if the value is an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Is_ok&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;determines if the value is an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Ok&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;let number = divide(103, 2);
if number.is_err() {
  println!(&quot;Error: {}&quot;, number.unwrap_err());
} else if number.is_ok() {
  println!(&quot;The answer is {}&quot;, number.unwrap());
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;All&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum values in Rust must be used or else we receive a warning from the compiler telling us that we have an unused&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum.&lt;/p&gt;
&lt;h3 id=&quot;option-enum&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum is used in Rust to represent an optional value. It has two variants:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Some&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;None&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Some&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is used when the input contains a value and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;represents the lack of a value. It is usually used with a pattern matching statement to handle the lack of a usable value from a function or expression.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;So here, we can modify the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;divide&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;function to use the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Options&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum instead of the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;:&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;fn divide(numerator: i32, denominator: i32) -&amp;gt; Option&amp;lt;i32&amp;gt; {
    if denominator == 0 {
        return None;
    } else {
        return Some(numerator / denominator);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;divide&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;function, we changed the return type from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;, so it returns&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;if the denominator is zero and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Some&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;with the result if the denominator is not zero.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then, we can use the new&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;divide&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;function in the following way:&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;fn main() {
    match divide(103, 0) {
        Some(solution) =&amp;gt; println!(&quot;The answer is {}&quot;, solution),
        None =&amp;gt; println!(&quot;Your numerator was about to be divided by zero :)&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this main function, we changed the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Ok&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;variant from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Some&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;from&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Options&lt;/span&gt;, and change&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Err&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;None&lt;/span&gt;.&lt;/p&gt;
&lt;div&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;More great articles from LogRocket:&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Don't miss a moment with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://lp.logrocket.com/subscribe-thereplay&quot;&gt;The Replay&lt;/a&gt;, a curated newsletter from LogRocket&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rethinking-error-tracking-product-analytics/&quot;&gt;Learn&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app&lt;/li&gt;
&lt;li&gt;Use React's useEffect&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://blog.logrocket.com/understanding-react-useeffect-cleanup-function/&quot;&gt;to optimize your application's performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Switch between&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://blog.logrocket.com/switching-between-node-versions-during-development/&quot;&gt;multiple versions of Node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/animate-react-app-animxyz/&quot;&gt;Discover how to animate&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;your React app with AnimXYZ&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-solid-js-tauri-desktop-app/&quot;&gt;Explore Tauri&lt;/a&gt;, a new framework for building binaries&lt;/li&gt;
&lt;li&gt;Compare&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://blog.logrocket.com/nestjs-vs-express-js/&quot;&gt;NestJS vs. Express.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And just like with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Result&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;enum, we can use the following:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;unwrap&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;method retrieves the value contained in a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Some&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;unwrap_or&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;method collects the data in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Some&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and returns a default if the expression is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;is_some&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;checks if it is not&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;is_none&lt;/span&gt;checks if the value is&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;None&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;fn main() {

    let number = divide(103, 0);

    if number.is_some() {
        println!(&quot;number is: {}&quot;, number.unwrap());
    } else {
        println!(&quot;Is the result none: {}&quot;, number.is_none());
        println!(&quot;Result: {}&quot;, number.unwrap_or(0));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot; data-ke-size=&quot;size26&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this article, we saw how enums and pattern matching works and how the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statement is more advanced than the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;statements.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And finally, we saw how we can use enums to improve pattern matching through holding data.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I hope this article has helped you fully understand how pattern matching and enums work. If there&amp;rsquo;s anything you do not understand, be sure to let me know in the comments.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thanks for reading and have a nice day!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from &lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.logrocket.com/rust-enums-and-pattern-matching/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1670218618083&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Rust enums and pattern matching - LogRocket Blog&quot; data-og-description=&quot;Learn the basics of pattern matching and enums in Rust, including error handling, handling null values, and more.&quot; data-og-host=&quot;blog.logrocket.com&quot; data-og-source-url=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/&quot; data-og-url=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcN54i/hyQNJMf1GU/iVDRKKI6Um0481kKdysPx0/img.png?width=730&amp;amp;height=487&amp;amp;face=0_0_730_487,https://scrap.kakaocdn.net/dn/yEsOM/hyQNYCC6tu/nxBUAPk4pOHQZNjWVAEjX0/img.png?width=1200&amp;amp;height=677&amp;amp;face=0_0_1200_677,https://scrap.kakaocdn.net/dn/Ri6X6/hyQNPr9YxK/f7JahOKvlQUXyBsnhfPyHk/img.png?width=730&amp;amp;height=487&amp;amp;face=0_0_730_487&quot;&gt;&lt;a href=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.logrocket.com/rust-enums-and-pattern-matching/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcN54i/hyQNJMf1GU/iVDRKKI6Um0481kKdysPx0/img.png?width=730&amp;amp;height=487&amp;amp;face=0_0_730_487,https://scrap.kakaocdn.net/dn/yEsOM/hyQNYCC6tu/nxBUAPk4pOHQZNjWVAEjX0/img.png?width=1200&amp;amp;height=677&amp;amp;face=0_0_1200_677,https://scrap.kakaocdn.net/dn/Ri6X6/hyQNPr9YxK/f7JahOKvlQUXyBsnhfPyHk/img.png?width=730&amp;amp;height=487&amp;amp;face=0_0_730_487');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Rust enums and pattern matching - LogRocket Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn the basics of pattern matching and enums in Rust, including error handling, handling null values, and more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.logrocket.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Rust</category>
      <category>backend</category>
      <category>rust</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/371</guid>
      <comments>https://brightlightkim.tistory.com/371#entry371comment</comments>
      <pubDate>Mon, 5 Dec 2022 14:37:01 +0900</pubDate>
    </item>
    <item>
      <title>Next.js doesn't call the local Rust API</title>
      <link>https://brightlightkim.tistory.com/370</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;If the public api call is something containing &quot;localhost,&quot; it's better to use &quot;0.0.0.0&quot; instead.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669405402573&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;NEXT_PUBLIC_API_URL = 'http://localhost:8080'

If it's like this and doesn't work, try:

NEXT_PUBLIC_API_URL = 'http://0.0.0.0:8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React/Next.js</category>
      <category>next.js</category>
      <category>nextjs</category>
      <category>rust</category>
      <category>RustAPI</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/370</guid>
      <comments>https://brightlightkim.tistory.com/370#entry370comment</comments>
      <pubDate>Sat, 26 Nov 2022 04:43:52 +0900</pubDate>
    </item>
    <item>
      <title>React .env not working</title>
      <link>https://brightlightkim.tistory.com/369</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Many developers use&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/facebook/create-react-app&quot;&gt;create-react-app&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;to generate a boilerplate needed to quickly start developing a React application.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It gives you the option to define Environment Variables is a special file named&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.env&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;All variables defined in this file are accessible in the code via&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;process.env.&amp;lt;VARIABLE_NAME&amp;gt;&lt;/b&gt;.&lt;/p&gt;
&lt;div id=&quot;Define-Environment-Variable&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Define Environment Variable&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, open the project and create&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.env&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file in the root directory:&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sekSX/btrQ0RTkgbR/IPjotppcpryTbf7Ah5l2Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sekSX/btrQ0RTkgbR/IPjotppcpryTbf7Ah5l2Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sekSX/btrQ0RTkgbR/IPjotppcpryTbf7Ah5l2Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsekSX%2FbtrQ0RTkgbR%2FIPjotppcpryTbf7Ah5l2Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;378&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;To define Environment Variable, open the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.env&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file and paste the following code:&lt;/p&gt;
&lt;div data-language=&quot;javascript&quot;&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;MY_ENVIRONMENT_VARIABLE=test&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We have just defined our first variable, great.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, save the changes, close the file and navigate to&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;App.jsx&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and read the variable:&lt;/p&gt;
&lt;div data-language=&quot;jsx&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {
  console.log(process.env.MY_ENVIRONMENT_VARIABLE);
  return &amp;lt;div className=&quot;App&quot;&amp;gt;Hello, world&amp;lt;/div&amp;gt;;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Start the project:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yarn start&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And note that&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;undefined&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;was output to the console:&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpDGMS/btrQ5lFx8Y0/Hpm7cc6p1BeU7LMewSUgy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpDGMS/btrQ5lFx8Y0/Hpm7cc6p1BeU7LMewSUgy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpDGMS/btrQ5lFx8Y0/Hpm7cc6p1BeU7LMewSUgy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpDGMS%2FbtrQ5lFx8Y0%2FHpm7cc6p1BeU7LMewSUgy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1194&quot; height=&quot;124&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;Do you know why?&lt;/p&gt;
&lt;div id=&quot;Gotcha-1&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Gotcha #1&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The first mistake we made is not reloading our app after defining an Environment Variable.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Remember that Environment Variables are embedded during build time.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Any change in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.env&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file requires an application to be reloaded.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let's do this and try again:&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yq6LW/btrQ1gywTeS/FVHP9NHSgxm5gsJCyCNwmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yq6LW/btrQ1gywTeS/FVHP9NHSgxm5gsJCyCNwmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yq6LW/btrQ1gywTeS/FVHP9NHSgxm5gsJCyCNwmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyq6LW%2FbtrQ1gywTeS%2FFVHP9NHSgxm5gsJCyCNwmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1194&quot; height=&quot;124&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;Still no success, let's find out why.&lt;/p&gt;
&lt;div id=&quot;Gotcha-2&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Gotcha #2&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The second thing to remember is that, for security reasons, create-react-app does not allow you to define Environment Variables that do not start with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;REACT_APP_&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;prefix.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Our variable name is definitely wrong and will not work.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let's change it:&lt;/p&gt;
&lt;div data-language=&quot;javascript&quot;&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;REACT_APP_MY_ENVIRONMENT_VARIABLE=test&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And in the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;App.jsx&lt;/b&gt;:&lt;/p&gt;
&lt;div data-language=&quot;jsx&quot;&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {
  console.log(process.env.REACT_APP_MY_ENVIRONMENT_VARIABLE);
  return &amp;lt;div className=&quot;App&quot;&amp;gt;Hello, world&amp;lt;/div&amp;gt;;
};

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reload an application and check the console:&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bekqyg/btrQ1lzL343/wyjssjRukJuDgjnIPj9OOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bekqyg/btrQ1lzL343/wyjssjRukJuDgjnIPj9OOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bekqyg/btrQ1lzL343/wyjssjRukJuDgjnIPj9OOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbekqyg%2FbtrQ1lzL343%2FwyjssjRukJuDgjnIPj9OOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1180&quot; height=&quot;120&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;Hooray, we got it to work.&lt;/p&gt;
&lt;div id=&quot;Summary&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Summary&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The two most important things to remember when defining Environment Variables in a project bootstrapped with create-react-app are:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Always reload your application after making a change to the&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.env&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;file&lt;/li&gt;
&lt;li&gt;Always prefix your variables with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;REACT_APP_&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>React</category>
      <category>.env</category>
      <category>react</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/369</guid>
      <comments>https://brightlightkim.tistory.com/369#entry369comment</comments>
      <pubDate>Mon, 14 Nov 2022 05:34:20 +0900</pubDate>
    </item>
    <item>
      <title>Android Threading: All You Need to Know</title>
      <link>https://brightlightkim.tistory.com/368</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Every Android developer, at one point or another, needs to deal with threads in their application.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When an application is launched in Android, it creates the first thread of execution, known as the &amp;ldquo;main&amp;rdquo; thread. The main thread is responsible for dispatching events to the appropriate user interface widgets as well as communicating with components from the Android UI toolkit.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To keep your application responsive, it is essential to avoid using the main thread to perform any operation that may end up keeping it blocked.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network operations and database calls, as well as loading of certain components, are common examples of operations that one should avoid in the main thread. When they are called in the main thread, they are called synchronously, which means that the UI will remain completely unresponsive until the operation completes. For this reason, they are usually performed in separate threads, which thereby avoids blocking the UI while they are being performed (i.e., they are performed asynchronously from the UI).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android provides many ways of creating and managing threads, and many third-party libraries exist that make thread management a lot more pleasant. However, with so many different approaches at hand, choosing the right one can be quite confusing.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this article, you will learn about some common scenarios in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.toptal.com/services/android-development&quot;&gt;Android development&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;where threading becomes essential and some simple solutions that can be applied to those scenarios and more.&lt;/p&gt;
&lt;h2 id=&quot;threading-in-android&quot; data-ke-size=&quot;size26&quot;&gt;Threading in Android&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In Android, you can categorize all threading components into two basic categories:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Threads that&lt;span&gt;&amp;nbsp;&lt;/span&gt;are&lt;span&gt;&amp;nbsp;&lt;/span&gt;attached to an activity/fragment:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;These threads are tied to the lifecycle of the activity/fragment and are terminated as soon as the activity/fragment is destroyed.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Threads that&lt;span&gt;&amp;nbsp;&lt;/span&gt;are not&lt;span&gt;&amp;nbsp;&lt;/span&gt;attached to any activity/fragment:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;These threads can continue to run beyond the lifetime of the activity/fragment (if any) from which they were spawned.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;threading-components-that-attach-to-an-activityfragment&quot; data-ke-size=&quot;size23&quot;&gt;Threading Components that Attach to an Activity/Fragment&lt;/h3&gt;
&lt;h4 id=&quot;asynctask&quot; data-ke-size=&quot;size20&quot;&gt;ASYNCTASK&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AsyncTask&lt;span&gt;&amp;nbsp;&lt;/span&gt;is the most basic Android component for threading. It&amp;rsquo;s simple to use and can be good for basic scenarios.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sample usage:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class ExampleActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        new MyTask().execute(url);
    }

    private class MyTask extends AsyncTask&amp;lt;String, Void, String&amp;gt; {

        @Override
        protected String doInBackground(String... params) {
            String url = params[0];
            return doSomeWork(url);
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            // do something with result 
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AsyncTask, however, falls short if you need your deferred task to run beyond the lifetime of the activity/fragment. It is worth noting that even something as simple as screen rotation can cause the activity to be destroyed.&lt;/p&gt;
&lt;h4 id=&quot;loaders&quot; data-ke-size=&quot;size20&quot;&gt;LOADERS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Loaders are the solution for the problem mentioned above. Loaders can automatically stop when the activity is destroyed, and can also restart themselves after the activity is recreated.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There are mainly two types of loaders:&lt;span&gt;&amp;nbsp;&lt;/span&gt;AsyncTaskLoader&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;CursorLoader. You will learn more about&lt;span&gt;&amp;nbsp;&lt;/span&gt;CursorLoader&lt;span&gt;&amp;nbsp;&lt;/span&gt;later in this article.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AsyncTaskLoader&lt;span&gt;&amp;nbsp;&lt;/span&gt;is similar to&lt;span&gt;&amp;nbsp;&lt;/span&gt;AsyncTask, but a bit more complicated.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sample usage:&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class ExampleActivity extends Activity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getLoaderManager().initLoader(1, null, new MyLoaderCallbacks());
    }
    
    private class MyLoaderCallbacks implements LoaderManager.LoaderCallbacks {

        @Override
        public Loader onCreateLoader(int id, Bundle args) {
            return new MyLoader(ExampleActivity.this);
        }

        @Override
        public void onLoadFinished(Loader loader, Object data) {

        }

        @Override
        public void onLoaderReset(Loader loader) {

        }
    }

    private class MyLoader extends AsyncTaskLoader {

        public MyLoader(Context context) {
            super(context);
        }

        @Override
        public Object loadInBackground() {
            return someWorkToDo();
        }
        
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;threading-components-that-dont-attach-to-an-activityfragment&quot; data-ke-size=&quot;size23&quot;&gt;Threading Components that Don&amp;rsquo;t Attach to an Activity/Fragment&lt;/h3&gt;
&lt;h4 id=&quot;service&quot; data-ke-size=&quot;size20&quot;&gt;SERVICE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a component that is useful for performing long (or potentially long) operations without any UI.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service&lt;span&gt;&amp;nbsp;&lt;/span&gt;runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sample usage:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class ExampleService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        doSomeLongProccesingWork();
        stopSelf();

        return START_NOT_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service, it is&lt;span&gt;&amp;nbsp;&lt;/span&gt;your&lt;span&gt;&amp;nbsp;&lt;/span&gt;responsibility to stop it when its work is complete by calling either the&lt;span&gt;&amp;nbsp;&lt;/span&gt;stopSelf()&lt;span&gt;&amp;nbsp;&lt;/span&gt;or the&lt;span&gt;&amp;nbsp;&lt;/span&gt;stopService()&lt;span&gt;&amp;nbsp;&lt;/span&gt;method.&lt;/p&gt;
&lt;h4 id=&quot;intentservice&quot; data-ke-size=&quot;size20&quot;&gt;INTENTSERVICE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Like&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service,&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService&lt;span&gt;&amp;nbsp;&lt;/span&gt;runs on a separate thread, and stops itself automatically after it completes its work.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntentService&lt;span&gt;&amp;nbsp;&lt;/span&gt;is usually used for short tasks that don&amp;rsquo;t need to be attached to any UI.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sample usage:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class ExampleService extends IntentService {
    
    public ExampleService() {
        super(&quot;ExampleService&quot;);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        doSomeShortWork();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;seven-threading-patterns-in-android&quot; data-ke-size=&quot;size26&quot;&gt;Seven Threading Patterns in Android&lt;/h2&gt;
&lt;h3 id=&quot;use-case-no-1-making-a-request-over-network-without-requiring-a-response-from-the-server&quot; data-ke-size=&quot;size23&quot;&gt;Use Case No. 1: Making a request over network without requiring a response from the server&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sometimes you may want to send an API request to a server without needing to worry about its response. For example, you may be sending a push registration token to your application&amp;rsquo;s back-end.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since this involves making a request over the network, you should do it from a thread other than the main thread.&lt;/p&gt;
&lt;h4 id=&quot;option-1-asynctask-or-loaders&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 1: ASYNCTASK OR LOADERS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You can use&lt;span&gt;&amp;nbsp;&lt;/span&gt;AsyncTask&lt;span&gt;&amp;nbsp;&lt;/span&gt;or loaders for making the call, and it will work.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However,&lt;span&gt;&amp;nbsp;&lt;/span&gt;AsyncTask&lt;span&gt;&amp;nbsp;&lt;/span&gt;and loaders are both dependent on the lifecycle of the activity. This means you will either need to wait for the call to execute and try to prevent the user from leaving the activity, or hope that it will execute before the activity is destroyed.&lt;/p&gt;
&lt;h4 id=&quot;option-2-service&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 2: SERVICE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service&lt;span&gt;&amp;nbsp;&lt;/span&gt;may be a better fit for this use case since it isn&amp;rsquo;t attached to any activity. It will therefore be able to continue with the network call even after the activity is destroyed. Plus, since the response from the server is not needed, a service wouldn&amp;rsquo;t be limiting here, either.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, since a service will begin running on the UI thread, you will still need to manage threading yourself. You will also need to make sure that the service is stopped once the network call is complete.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This would require more effort than should be necessary for such a simple action.&lt;/p&gt;
&lt;h4 id=&quot;option-3-intentservice&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 3: INTENTSERVICE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This, in my opinion, would be the best option.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService&lt;span&gt;&amp;nbsp;&lt;/span&gt;doesn&amp;rsquo;t attach to any activity and it runs on a non-UI thread, it serves our needs perfectly here. Moreover,&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService&lt;span&gt;&amp;nbsp;&lt;/span&gt;stops itself automatically, so there is no need to manually manage it, either.&lt;/p&gt;
&lt;h3 id=&quot;use-case-no-2-making-a-network-call-and-getting-the-response-from-the-server&quot; data-ke-size=&quot;size23&quot;&gt;Use Case No. 2: Making a network call, and getting the response from the server&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This use case is probably a bit more common. For example, you may want to invoke an API in the back-end and use its response to populate fields on the screen.&lt;/p&gt;
&lt;h4 id=&quot;option-1-service-or-intentservice&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 1: SERVICE OR INTENTSERVICE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Although a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service&lt;span&gt;&amp;nbsp;&lt;/span&gt;or an&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService&lt;span&gt;&amp;nbsp;&lt;/span&gt;fared well for the previous use case, using them here wouldn&amp;rsquo;t be a good idea. Trying to get data out of a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service&lt;span&gt;&amp;nbsp;&lt;/span&gt;or an&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService&lt;span&gt;&amp;nbsp;&lt;/span&gt;into the main UI thread would make things very complex.&lt;/p&gt;
&lt;h4 id=&quot;option-2-asynctask-or-loaders&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 2: ASYNCTASK OR LOADERS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;At first blush,&lt;span&gt;&amp;nbsp;&lt;/span&gt;AsyncTask&lt;span&gt;&amp;nbsp;&lt;/span&gt;or loaders would appear to be the obvious solution here. They are easy to use&amp;mdash;simple and straightforward.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, when using&lt;span&gt;&amp;nbsp;&lt;/span&gt;AsyncTask&lt;span&gt;&amp;nbsp;&lt;/span&gt;or loaders, you&amp;rsquo;ll notice that there is a need to write some boilerplate code. Moreover, error handling becomes a major chore with these components. Even with a simple networking call, you need to be aware of potential exceptions, catch them, and act accordingly. This forces us to wrap our response in a custom class containing the data, with possible error information, and a flag indicates if the action was successful or not.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That&amp;rsquo;s quite a lot of work to do for every single call. Fortunately, there is now a much better and simpler solution available: RxJava.&lt;/p&gt;
&lt;h4 id=&quot;option-3-rxjava&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 3: RXJAVA&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You may heard about&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/ReactiveX/RxJava&quot;&gt;RxJava&lt;/a&gt;, the library developed by Netflix. It&amp;rsquo;s almost magic in Java.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ReactiveX/RxAndroid&quot;&gt;RxAndroid&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;lets you use RxJava in Android, and makes dealing with asynchronous tasks a breeze. You can learn more about RxJava on Android&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.toptal.com/android/functional-reactive-android-rxjava&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxJava provides two components:&lt;span&gt;&amp;nbsp;&lt;/span&gt;Observer&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;Subscriber.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;An&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;observer&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a component that contains some action. It performs that action and returns the result if it succeeds or an error if it fails.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;subscriber&lt;/b&gt;, on the other hand, is a component that can receive the result (or error) from an observable, by subscribing to it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With RxJava, you first create an observable:&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;Observable.create((ObservableOnSubscribe&amp;lt;Data&amp;gt;) e -&amp;gt; {
    Data data = mRestApi.getData();
    e.onNext(data);
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Once the observable has been created, you can subscribe to it.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With the RxAndroid library, you can control the thread in which you want to execute the action in the observable, and the thread in which you want to get the response (i.e., the result or error).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You chain on the observable with these two functions:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Schedulers are components that execute the action in a certain thread.&lt;span&gt;&amp;nbsp;&lt;/span&gt;AndroidSchedulers.mainThread()&lt;span&gt;&amp;nbsp;&lt;/span&gt;is the scheduler associated with the main thread.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Given that our API call is&lt;span&gt;&amp;nbsp;&lt;/span&gt;mRestApi.getData()&lt;span&gt;&amp;nbsp;&lt;/span&gt;and it returns a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Data&lt;span&gt;&amp;nbsp;&lt;/span&gt;object, the basic call can look like this:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Observable.create((ObservableOnSubscribe&amp;lt;Data&amp;gt;) e -&amp;gt; {
            try { 
                        Data data = mRestApi.getData();
                        e.onNext(data);
            } catch (Exception ex) {
                        e.onError(ex);
            }
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(match -&amp;gt; Log.i(&amp;ldquo;rest api, &quot;success&quot;),
            throwable -&amp;gt; Log.e(&amp;ldquo;rest api, &quot;error: %s&quot; + throwable.getMessage()));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Without even going into other benefits of using RxJava, you can already see how RxJava allows us to write more mature code by abstracting away the complexity of threading.&lt;/p&gt;
&lt;h3 id=&quot;use-case-no-3-chaining-network-calls&quot; data-ke-size=&quot;size23&quot;&gt;Use Case No. 3: Chaining network calls&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For network calls that need to be performed in sequence (i.e., where each operation depends upon the response/result of the previous operation), you need to be particularly careful about generating spaghetti code.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For example, you may have to make an API call with a token that you need to fetch first through another API call.&lt;/p&gt;
&lt;h4 id=&quot;option-1-asynctask-or-loaders-1&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 1: ASYNCTASK OR LOADERS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Using&lt;span&gt;&amp;nbsp;&lt;/span&gt;AsyncTask&lt;span&gt;&amp;nbsp;&lt;/span&gt;or loaders will almost definitely lead to spaghetti code. The overall functionality will be difficult to get right and will require a tremendous amount of redundant boilerplate code throughout your project.&lt;/p&gt;
&lt;h4 id=&quot;option-2-rxjava-using-flatmap&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 2: RXJAVA USING FLATMAP&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In RxJava, the&lt;span&gt;&amp;nbsp;&lt;/span&gt;flatMap&lt;span&gt;&amp;nbsp;&lt;/span&gt;operator takes an emitted value from the source observable and returns another observable. You can create an observable, and then create another observable using the emitted value from the first one, which will basically chain them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 1.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Create the observable that fetches the token:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public Observable&amp;lt;String&amp;gt; getTokenObservable() {
    return Observable.create(subscriber -&amp;gt; {
        try {
            String token = mRestApi.getToken();
            subscriber.onNext(token);

        } catch (IOException e) {
            subscriber.onError(e);
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 2.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Create the observable that gets the data using the token:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public Observable&amp;lt;String&amp;gt; getDataObservable(String token) {
    return Observable.create(subscriber -&amp;gt; {
        try {
            Data data = mRestApi.getData(token);
            subscriber.onNext(data);

        } catch (IOException e) {
            subscriber.onError(e);
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 3.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Chain the two observables together and subscribe:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;getTokenObservable()
.flatMap(new Function&amp;lt;String, Observable&amp;lt;Data&amp;gt;&amp;gt;() {
    @Override
    public Observable&amp;lt;Data&amp;gt; apply(String token) throws Exception {
        return getDataObservable(token);
    }
})
.subscribe(data -&amp;gt; {
    doSomethingWithData(data)
}, error -&amp;gt; handleError(e));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Note that use of this approach is not limited to network calls; it can work with any set of actions that needs to be run in a sequence but on separate threads.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;All of the use cases above are quite simple. Switching between threads only happened after each finished its task. More advanced scenarios&amp;mdash;for example, where two or more threads need to actively communicate with each other&amp;mdash;can be supported by this approach as well.&lt;/p&gt;
&lt;h3 id=&quot;use-case-no-4-communicate-with-the-ui-thread-from-another-thread&quot; data-ke-size=&quot;size23&quot;&gt;Use Case No. 4: Communicate with the UI thread from another thread&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Consider a scenario where you would like to upload a file and update the user interface once it is complete.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since uploading a file can take a long time, there is no need to keep the user waiting. You could use a service, and probably&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService, for implementing the functionality here.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this case, however, the bigger challenge is being able to invoke a method on the UI thread after the file upload (which was performed in a separate thread) is complete.&lt;/p&gt;
&lt;h4 id=&quot;option-1-rxjava-inside-the-service&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 1: RXJAVA INSIDE THE SERVICE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxJava, either on its own or inside an&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService, may not be ideal. You will need to use a callback-based mechanism when subscribing to an&lt;span&gt;&amp;nbsp;&lt;/span&gt;Observable, and&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentService&lt;span&gt;&amp;nbsp;&lt;/span&gt;is built to do simple synchronous calls, not callbacks.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;On the other hand, with a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service, you will need to manually stop the service, which requires more work.&lt;/p&gt;
&lt;h4 id=&quot;option-2-broadcastreceiver&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 2: BROADCASTRECEIVER&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android provides this component, which can listen to global events (e.g., battery events, network events, etc.) as well as custom events. You can use this component to create a custom event that is triggered when the upload is complete.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To do this, you need to create a custom class that extends&lt;span&gt;&amp;nbsp;&lt;/span&gt;BroadcastReceiver, register it in the manifest, and use&lt;span&gt;&amp;nbsp;&lt;/span&gt;Intent&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntentFilter&lt;span&gt;&amp;nbsp;&lt;/span&gt;to create the custom event. To trigger the event, you will need the&lt;span&gt;&amp;nbsp;&lt;/span&gt;sendBroadcast&lt;span&gt;&amp;nbsp;&lt;/span&gt;method.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Manifest:&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;receiver android:name=&quot;UploadReceiver&quot;&amp;gt;
   
    &amp;lt;intent-filter&amp;gt;
        &amp;lt;action android:name=&quot;com.example.upload&quot;&amp;gt;
        &amp;lt;/action&amp;gt;
    &amp;lt;/intent-filter&amp;gt;
   
&amp;lt;/receiver&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class UploadReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getBoolean(&amp;ldquo;success&amp;rdquo;, false) {
            Activity activity = (Activity)context;
            activity.updateUI();
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Intent intent = new Intent();
intent.setAction(&quot;com.example.upload&quot;); 
sendBroadcast(intent);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This approach is a viable option. But as you have noticed, it involves some work, and too many broadcasts can slow things down.&lt;/p&gt;
&lt;h4 id=&quot;option-3-using-handler&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 3: USING HANDLER&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;Handler&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a component that can be attached to a thread and then made to perform some action on that thread via simple messages or&lt;span&gt;&amp;nbsp;&lt;/span&gt;Runnable&lt;span&gt;&amp;nbsp;&lt;/span&gt;tasks. It works in conjunction with another component,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Looper, which is in charge of message processing in a particular thread.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Handler&lt;span&gt;&amp;nbsp;&lt;/span&gt;is created, it can get a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Looper&lt;span&gt;&amp;nbsp;&lt;/span&gt;object in the constructor, which indicates which thread the handler is attached to. If you want to use a handler attached to the main thread, you need to use the looper associated with the main thread by calling&lt;span&gt;&amp;nbsp;&lt;/span&gt;Looper.getMainLooper().&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this case, to update the UI from a background thread, you can create a handler attached to the UI thread, and then post an action as a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Runnable:&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            // update the ui from here                
        }
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This approach is a lot better than the first one, but there is an even simpler way to do this&amp;hellip;&lt;/p&gt;
&lt;h4 id=&quot;option-3-using-eventbus&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 3: USING EVENTBUS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/greenrobot/EventBus&quot;&gt;EventBus&lt;/a&gt;, a popular library by GreenRobot, enables components to safely communicate with one another. Since our use case is one where we only want to update the UI, this can be the simplest and safest choice.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 1.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Create an event class. e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;UIEvent.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 2.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Subscribe to the event.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Subscribe(threadMode = ThreadMode.MAIN)  
public void onUIEvent(UIEvent event) {/* Do something */};

 register and unregister eventbus : 

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    super.onStop();
    EventBus.getDefault().unregister(this);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 3.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Post the event:&lt;span&gt;&amp;nbsp;&lt;/span&gt;EventBus.getDefault().post(new UIEvent());&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With the&lt;span&gt;&amp;nbsp;&lt;/span&gt;ThreadMode&lt;span&gt;&amp;nbsp;&lt;/span&gt;parameter in the annotation, you are specifying the thread on which you would like to subscribe for this event. In our example here, we are choosing the main thread, since we will want the receiver of the event to be able to update the UI.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You can structure your&lt;span&gt;&amp;nbsp;&lt;/span&gt;UIEvent&lt;span&gt;&amp;nbsp;&lt;/span&gt;class to contain additional information as necessary.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the service:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class UploadFileService extends IntentService {
    // &amp;hellip;
    Boolean success = uploadFile(File file);
    EventBus.getDefault().post(new UIEvent(success));
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the activity/fragment:&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Subscribe(threadMode = ThreadMode.MAIN)  
public void onUIEvent(UIEvent event) {//show message according to the action success};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;EventBus library, communicating between threads becomes much simpler.&lt;/p&gt;
&lt;h3 id=&quot;use-case-no-5-two-way-communication-between-threads-based-on-user-actions&quot; data-ke-size=&quot;size23&quot;&gt;Use Case No. 5: Two-way communication between threads based on user actions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Suppose you are building a media player and you want it to be able to continue playing music even when the application screen is closed. In this scenario, you will want the UI to be able to communicate with the media thread (e.g., play, pause, and other actions) and will also want the media thread to update the UI based on certain events (e.g. error, buffering status, etc).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A full media player example is beyond the scope of this article. You can, however, find good tutorials&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/googlesamples/android-UniversalMusicPlayer&quot;&gt;here&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/google/ExoPlayer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;option-1-using-eventbus&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 1: USING EVENTBUS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You could use&lt;span&gt;&amp;nbsp;&lt;/span&gt;EventBus&lt;span&gt;&amp;nbsp;&lt;/span&gt;here. However, it is generally unsafe to post an event from the UI thread and receive it in a service. This is because you have no way of knowing whether the service is running when you have sent the message.&lt;/p&gt;
&lt;h4 id=&quot;option-2-using-boundservice&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 2: USING BOUNDSERVICE&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;BoundService&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Service&lt;span&gt;&amp;nbsp;&lt;/span&gt;that is bound to an activity/fragment. This means that the activity/fragment always knows if the service is running or not and, in addition, it gets access to the service&amp;rsquo;s public methods.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To implement it, you need to create a custom&lt;span&gt;&amp;nbsp;&lt;/span&gt;Binder&lt;span&gt;&amp;nbsp;&lt;/span&gt;inside the service and create a method that returns the service.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class MediaService extends Service {

    private final IBinder mBinder = new MediaBinder();

    public class MediaBinder extends Binder {
        MediaService getService() {
            // Return this instance of LocalService so clients can call public methods
            return MediaService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To bind the activity to the service, you need to implement&lt;span&gt;&amp;nbsp;&lt;/span&gt;ServiceConnection, which is the class monitoring the service status, and use the method&lt;span&gt;&amp;nbsp;&lt;/span&gt;bindService&lt;span&gt;&amp;nbsp;&lt;/span&gt;to make the binding:&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;// in the activity
MediaService mService;
// flag indicates the bound status
boolean mBound;

@Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, MediaService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {

            MediaBinder binder = (MediaBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You can find a full implementation example&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.android.com/guide/components/bound-services.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To communicate with the service when the user taps on the Play or Pause button, you can bind to the service and then call the relevant public method on the service.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When there is a media event and you want to communicate that back to the activity/fragment, you can use one of the earlier techniques (e.g.,&lt;span&gt;&amp;nbsp;&lt;/span&gt;BroadcastReceiver,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Handler, or&lt;span&gt;&amp;nbsp;&lt;/span&gt;EventBus).&lt;/p&gt;
&lt;h3 id=&quot;use-case-no-6-executing-actions-in-parallel-and-getting-results&quot; data-ke-size=&quot;size23&quot;&gt;Use Case No. 6: Executing actions in parallel and getting results&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let&amp;rsquo;s say you are building a tourist app and you want to show attractions on a map fetched from multiple sources (different data providers). Since not all of the sources may be reliable, you may want to ignore the ones that have failed and continue to render the map anyway.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To parallelize the process, each API call must take place in a different thread.&lt;/p&gt;
&lt;h4 id=&quot;option-1-using-rxjava&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 1: USING RXJAVA&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In RxJava, you can combine multiple observables into one using the&lt;span&gt;&amp;nbsp;&lt;/span&gt;merge()&lt;span&gt;&amp;nbsp;&lt;/span&gt;or&lt;span&gt;&amp;nbsp;&lt;/span&gt;concat()&lt;span&gt;&amp;nbsp;&lt;/span&gt;operators. You can then subscribe on the &amp;ldquo;merged&amp;rdquo; observable and wait for all results.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This approach, however, won&amp;rsquo;t work as expected. If one API call fails, the merged observable will report an overall failure.&lt;/p&gt;
&lt;h4 id=&quot;option-2-using-native-java-components&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 2: USING NATIVE JAVA COMPONENTS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The&lt;span&gt;&amp;nbsp;&lt;/span&gt;ExecutorService&lt;span&gt;&amp;nbsp;&lt;/span&gt;in Java creates a fixed (configurable) number of threads and executes tasks on them concurrently. The service returns a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Future&lt;span&gt;&amp;nbsp;&lt;/span&gt;object that eventually returns all results via the&lt;span&gt;&amp;nbsp;&lt;/span&gt;invokeAll()&lt;span&gt;&amp;nbsp;&lt;/span&gt;method.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Each task you send to the&lt;span&gt;&amp;nbsp;&lt;/span&gt;ExecutorService&lt;span&gt;&amp;nbsp;&lt;/span&gt;should be contained in&lt;span&gt;&amp;nbsp;&lt;/span&gt;Callable&lt;span&gt;&amp;nbsp;&lt;/span&gt;interface, which is an interface for creating a task that can throw an exception.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Once you get the results from&lt;span&gt;&amp;nbsp;&lt;/span&gt;invokeAll(), you can check every result and proceed accordingly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let&amp;rsquo;s say, for example, that you have three attraction types coming in from three different endpoints and you want to make three parallel calls:&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;ExecutorService pool = Executors.newFixedThreadPool(3);
    List&amp;lt;Callable&amp;lt;Object&amp;gt;&amp;gt; tasks = new ArrayList&amp;lt;&amp;gt;();
    tasks.add(new Callable&amp;lt;Object&amp;gt;() {
        @Override
        public Integer call() throws Exception {
            return mRest.getAttractionType1();
        }
    });

    // ...

    try {
        List&amp;lt;Future&amp;lt;Object&amp;gt;&amp;gt; results = pool.invokeAll(tasks);
        for (Future result : results) {
        try {
            Object response = result.get();
            if (response instance of AttractionType1... {}
            if (response instance of AttractionType2... {}
                ...
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This way, you are running all of the actions in parallel. You can, therefore, check for errors in each action separately and ignore individual failures as appropriate.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This approach is easier than using RxJava. It is simpler, shorter, and doesn&amp;rsquo;t fail all actions because of one exception.&lt;/p&gt;
&lt;h3 id=&quot;use-case-7-querying-local-sqlite-database&quot; data-ke-size=&quot;size23&quot;&gt;Use Case #7: Querying local SQLite database&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When dealing with a local SQLite database, it is recommended that the database be used from a background thread, since database calls (especially with large databases or complex queries) can be time consuming, resulting in the UI freezing.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When querying for SQLite data, you get a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Cursor&lt;span&gt;&amp;nbsp;&lt;/span&gt;object that can then be used to fetch the actual data.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Cursor cursor = getData();
String name = cursor.getString(&amp;lt;colum_number&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;option-1-using-rxjava-1&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 1: USING RXJAVA&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You can use RxJava and get the data from the database, just as we&amp;rsquo;re getting data from our back-end:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;public Observable&amp;lt;Cursor&amp;gt; getLocalDataObservable() {
    return Observable.create(subscriber -&amp;gt; {
        Cursor cursor = mDbHandler.getData();
        subscriber.onNext(cursor);
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You can use the observable returned by&lt;span&gt;&amp;nbsp;&lt;/span&gt;getLocalDataObservable()&lt;span&gt;&amp;nbsp;&lt;/span&gt;as follows:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;getLocalDataObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
 .subscribe(cursor -&amp;gt; String name = cursor.getString(0),
                   throwable -&amp;gt; Log.e(&amp;ldquo;db, &quot;error: %s&quot; + throwable.getMessage()));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;While this is certainly a good approach, there is one that is even better, since there is a component that is built just for this very scenario.&lt;/p&gt;
&lt;h4 id=&quot;option-2-using-cursorloader--contentprovider&quot; data-ke-size=&quot;size20&quot;&gt;OPTION 2: USING CURSORLOADER + CONTENTPROVIDER&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android provides&lt;span&gt;&amp;nbsp;&lt;/span&gt;CursorLoader, a native component for loading SQLite data and managing the corresponding thread. It&amp;rsquo;s a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Loader&lt;span&gt;&amp;nbsp;&lt;/span&gt;that returns a&lt;span&gt;&amp;nbsp;&lt;/span&gt;Cursor, which we can use to get the data by calling simple methods such as&lt;span&gt;&amp;nbsp;&lt;/span&gt;getString(),&lt;span&gt;&amp;nbsp;&lt;/span&gt;getLong(), etc.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class SimpleCursorLoader extends FragmentActivity implements
LoaderManager.LoaderCallbacks&amp;lt;Cursor&amp;gt; {

  public static final String TAG = SimpleCursorLoader.class.getSimpleName();
  private static final int LOADER_ID = 0x01;
  private TextView textView;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_cursor_loader);
    textView = (TextView) findViewById(R.id.text_view);
    getSupportLoaderManager().initLoader(LOADER_ID, null, this);

  }

  public Loader&amp;lt;Cursor&amp;gt; onCreateLoader(int i, Bundle bundle) {

    return new CursorLoader(this,
      Uri.parse(&quot;content://com.github.browep.cursorloader.data&quot;)
      , new String[]{&quot;col1&quot;}, null, null, null);
    }

    public void onLoadFinished(Loader&amp;lt;Cursor&amp;gt; cursorLoader, Cursor cursor) {
      if (cursor != null &amp;amp;&amp;amp; cursor.moveToFirst()) {
        String text =  textView.getText().toString();
        while (cursor.moveToNext()) {
          text += &quot;&amp;lt;br /&amp;gt;&quot; + cursor.getString(1);
          cursor.moveToNext();
        }
        textView.setText(Html.fromHtml(text) );
      }
    }

    public void onLoaderReset(Loader&amp;lt;Cursor&amp;gt; cursorLoader) {
      
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CursorLoader&lt;span&gt;&amp;nbsp;&lt;/span&gt;works with the&lt;span&gt;&amp;nbsp;&lt;/span&gt;ContentProvider&lt;span&gt;&amp;nbsp;&lt;/span&gt;component. This component provides a plethora of real-time database features (e.g., change notifications, triggers, etc.) that enables developers to implement a better user experience much more easily.&lt;/p&gt;
&lt;h2 id=&quot;theres-no-silver-bullet-solution-to-threading-in-android&quot; data-ke-size=&quot;size26&quot;&gt;There&amp;rsquo;s no Silver Bullet Solution to Threading in Android&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android provides many ways to handle and manage threads, but none of them are silver bullets.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Choosing the right threading approach, depending on your use case, can make all the difference in making the overall solution easy to implement and understand. The native components fit well for some cases, but not for all. The same applies for fancy third-party solutions.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I hope you will find this article useful when working on your next Android project. Share with us your experience of threading in Android or any use case where the above solutions work well&amp;mdash;or don&amp;rsquo;t, for that matter&amp;mdash;in the comments below.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;from &lt;a href=&quot;https://www.toptal.com/android/android-threading-all-you-need-to-know#:~:text=When%20an%20application%20is%20launched,from%20the%20Android%20UI%20toolkit.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.toptal.com/android/android-threading-all-you-need-to-know#:~:text=When%20an%20application%20is%20launched,from%20the%20Android%20UI%20toolkit.&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1668189936421&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Android Threading: All You Need to Know&quot; data-og-description=&quot;Android provides many ways of creating and managing threads, and third-party libraries exist to make that even easier. However, with so many options, choosing the right approach can be quite confusing. In this article, Toptal Freelance Software Engineer El&quot; data-og-host=&quot;www.toptal.com&quot; data-og-source-url=&quot;https://www.toptal.com/android/android-threading-all-you-need-to-know#:~:text=When%20an%20application%20is%20launched,from%20the%20Android%20UI%20toolkit.&quot; data-og-url=&quot;https://www.toptal.com/android/android-threading-all-you-need-to-know&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/e1sUG/hyQxAPJeaB/SKEeQSKrddz0Y3OYP5loik/img.png?width=1720&amp;amp;height=900&amp;amp;face=0_0_1720_900,https://scrap.kakaocdn.net/dn/bmQb1H/hyQxBOEvDL/B9wcje4mlwkeXeKzptt2YK/img.png?width=1720&amp;amp;height=900&amp;amp;face=0_0_1720_900&quot;&gt;&lt;a href=&quot;https://www.toptal.com/android/android-threading-all-you-need-to-know#:~:text=When%20an%20application%20is%20launched,from%20the%20Android%20UI%20toolkit.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.toptal.com/android/android-threading-all-you-need-to-know#:~:text=When%20an%20application%20is%20launched,from%20the%20Android%20UI%20toolkit.&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/e1sUG/hyQxAPJeaB/SKEeQSKrddz0Y3OYP5loik/img.png?width=1720&amp;amp;height=900&amp;amp;face=0_0_1720_900,https://scrap.kakaocdn.net/dn/bmQb1H/hyQxBOEvDL/B9wcje4mlwkeXeKzptt2YK/img.png?width=1720&amp;amp;height=900&amp;amp;face=0_0_1720_900');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Android Threading: All You Need to Know&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Android provides many ways of creating and managing threads, and third-party libraries exist to make that even easier. However, with so many options, choosing the right approach can be quite confusing. In this article, Toptal Freelance Software Engineer El&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.toptal.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Android_App/Back-end</category>
      <category>Android</category>
      <category>APP</category>
      <category>kotlin</category>
      <category>mobile</category>
      <category>Threading</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/368</guid>
      <comments>https://brightlightkim.tistory.com/368#entry368comment</comments>
      <pubDate>Sat, 12 Nov 2022 03:05:49 +0900</pubDate>
    </item>
    <item>
      <title>[RxJava] Managing State with RxJava</title>
      <link>https://brightlightkim.tistory.com/367</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vyMCL/btrQQbQjjAl/qHEuLHTrbK3LEmYIoN0E1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vyMCL/btrQQbQjjAl/qHEuLHTrbK3LEmYIoN0E1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vyMCL/btrQQbQjjAl/qHEuLHTrbK3LEmYIoN0E1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvyMCL%2FbtrQQbQjjAl%2FqHEuLHTrbK3LEmYIoN0E1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1608&quot; height=&quot;836&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3J98z/btrQNHXIgdF/7klABnlmMwdLacdPP7mkbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3J98z/btrQNHXIgdF/7klABnlmMwdLacdPP7mkbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3J98z/btrQNHXIgdF/7klABnlmMwdLacdPP7mkbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3J98z%2FbtrQNHXIgdF%2F7klABnlmMwdLacdPP7mkbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1417&quot; height=&quot;797&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;What if there are more async calls, there are a number of cases we should care about.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1473&quot; data-origin-height=&quot;823&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6vJPv/btrQMsGt3yu/ARvCc1YW32VVsQqM37F7WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6vJPv/btrQMsGt3yu/ARvCc1YW32VVsQqM37F7WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6vJPv/btrQMsGt3yu/ARvCc1YW32VVsQqM37F7WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6vJPv%2FbtrQMsGt3yu%2FARvCc1YW32VVsQqM37F7WK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1473&quot; height=&quot;823&quot; data-origin-width=&quot;1473&quot; data-origin-height=&quot;823&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;When there are a number of cases.. in the Mobile Development&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1I8EH/btrQQjOiSTB/ILPVJeLc5PatfljdEtHxRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1I8EH/btrQQjOiSTB/ILPVJeLc5PatfljdEtHxRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1I8EH/btrQQjOiSTB/ILPVJeLc5PatfljdEtHxRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1I8EH%2FbtrQQjOiSTB%2FILPVJeLc5PatfljdEtHxRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1249&quot; height=&quot;661&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Everything in Android is async.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd2FcN/btrQPC8sAJp/heghd0hEu5QpE0q4lBIEP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd2FcN/btrQPC8sAJp/heghd0hEu5QpE0q4lBIEP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd2FcN/btrQPC8sAJp/heghd0hEu5QpE0q4lBIEP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd2FcN%2FbtrQPC8sAJp%2Fheghd0hEu5QpE0q4lBIEP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1318&quot; height=&quot;830&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The activity life cycle is reactive. RxJava solves this problem.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVSuGc/btrQOak5Ent/aZ75UPTdaOGS7IAJdNk5E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVSuGc/btrQOak5Ent/aZ75UPTdaOGS7IAJdNk5E1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVSuGc/btrQOak5Ent/aZ75UPTdaOGS7IAJdNk5E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVSuGc%2FbtrQOak5Ent%2FaZ75UPTdaOGS7IAJdNk5E1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1269&quot; height=&quot;846&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;846&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxJava can remove the intermediator code. It can connect directly to its components&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DCPJL/btrQNJ89oaW/irGZOGBN6OVM2BekGYJcc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DCPJL/btrQNJ89oaW/irGZOGBN6OVM2BekGYJcc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DCPJL/btrQNJ89oaW/irGZOGBN6OVM2BekGYJcc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDCPJL%2FbtrQNJ89oaW%2FirGZOGBN6OVM2BekGYJcc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;315&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observable: Any time the user update the value with the user, we can do something with it&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Completable: It can receive failure or not.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1762&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6Eqih/btrQRFQ7isu/HbgIxpVeGKFLk61xamS3a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6Eqih/btrQRFQ7isu/HbgIxpVeGKFLk61xamS3a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6Eqih/btrQRFQ7isu/HbgIxpVeGKFLk61xamS3a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6Eqih%2FbtrQRFQ7isu%2FHbgIxpVeGKFLk61xamS3a0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1762&quot; height=&quot;495&quot; data-origin-width=&quot;1762&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;disposables &amp;gt;&amp;gt; lifecycle to disconnect from the componenets&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;629&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpLp1A/btrQRmjKi9S/KvezlwnXT35y5cNwJfC0uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpLp1A/btrQRmjKi9S/KvezlwnXT35y5cNwJfC0uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpLp1A/btrQRmjKi9S/KvezlwnXT35y5cNwJfC0uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpLp1A%2FbtrQRmjKi9S%2FKvezlwnXT35y5cNwJfC0uK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1548&quot; height=&quot;629&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;629&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Use the disposable list &amp;gt; disconnect it.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1737&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c84jIx/btrQRGvItnc/krJo0ZJMWWD3U9xCjDs1M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c84jIx/btrQRGvItnc/krJo0ZJMWWD3U9xCjDs1M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c84jIx/btrQRGvItnc/krJo0ZJMWWD3U9xCjDs1M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc84jIx%2FbtrQRGvItnc%2FkrJo0ZJMWWD3U9xCjDs1M0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1737&quot; height=&quot;800&quot; data-origin-width=&quot;1737&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Whenever data is pushed in, you never have to pull it out.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;State Problem&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1773&quot; data-origin-height=&quot;1011&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXpb2j/btrQOzYSYmE/6tEjxKhFiwxaFujok673OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXpb2j/btrQOzYSYmE/6tEjxKhFiwxaFujok673OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXpb2j/btrQOzYSYmE/6tEjxKhFiwxaFujok673OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXpb2j%2FbtrQOzYSYmE%2F6tEjxKhFiwxaFujok673OK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1773&quot; height=&quot;1011&quot; data-origin-width=&quot;1773&quot; data-origin-height=&quot;1011&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1789&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7xyWQ/btrQOP8fOd2/24njd6pZSmlZCvp15S3cFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7xyWQ/btrQOP8fOd2/24njd6pZSmlZCvp15S3cFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7xyWQ/btrQOP8fOd2/24njd6pZSmlZCvp15S3cFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7xyWQ%2FbtrQOP8fOd2%2F24njd6pZSmlZCvp15S3cFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1789&quot; height=&quot;702&quot; data-origin-width=&quot;1789&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clicks: turning them into string everytime they clicks it&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Then after that disable click one.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flatMap: take items emissions and move them into observable that folded get back into the stream&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if it's succeed&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If it's an error and check it.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;However, it takes a lot of trouble.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Android_App/RxJava</category>
      <author>brightlightkim</author>
      <guid isPermaLink="true">https://brightlightkim.tistory.com/367</guid>
      <comments>https://brightlightkim.tistory.com/367#entry367comment</comments>
      <pubDate>Fri, 11 Nov 2022 04:30:33 +0900</pubDate>
    </item>
  </channel>
</rss>