<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lettucech</title>
    <description>The latest articles on DEV Community by Lettucech (@lettucech).</description>
    <link>https://dev.to/lettucech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3375834%2Fc58d9a7b-8c3f-47c1-af5f-424ee9efac65.jpeg</url>
      <title>DEV Community: Lettucech</title>
      <link>https://dev.to/lettucech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lettucech"/>
    <language>en</language>
    <item>
      <title>[太長;不看... EP2] Android MVI 要點</title>
      <dc:creator>Lettucech</dc:creator>
      <pubDate>Thu, 09 Oct 2025 08:56:09 +0000</pubDate>
      <link>https://dev.to/lettucech/tai-chang-bu-kan-ep2-android-mvi-yao-dian-25bl</link>
      <guid>https://dev.to/lettucech/tai-chang-bu-kan-ep2-android-mvi-yao-dian-25bl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;由 AI 協肋生成，文筆可能會有不順暢的情況出現。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  到底在說什麼？
&lt;/h1&gt;

&lt;p&gt;MVI (Model-View-Intent) 是 Android 複雜頁面或多人協作專案的&lt;strong&gt;頂級解方&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;核心精髓： UDF (單向數據流) 永遠只朝一個方向跑，確保狀態可預測。&lt;/li&gt;
&lt;li&gt;MVI = 狀態即真理： 整個畫面只依賴一個唯一的、不可變的 State 物件。&lt;/li&gt;
&lt;li&gt;現代化對應：

&lt;ul&gt;
&lt;li&gt;Intent：使用者在 Composable 上的所有動作。&lt;/li&gt;
&lt;li&gt;View Model：負責處理 Intent，並釋出新的 State。&lt;/li&gt;
&lt;li&gt;Composable：純粹的畫面渲染器 (Render)，只讀 State。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  MVI 的黃金循環
&lt;/h1&gt;

&lt;p&gt;MVI 就像一個不斷運轉的單向輸送帶，所有資料都不能逆流。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuhr1n21ydf1se87lb2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuhr1n21ydf1se87lb2o.png" alt=" " width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  簡短程式碼示例 (概念模型)
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;物件&lt;/th&gt;
&lt;th&gt;從屬關係&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ScreenState&lt;/td&gt;
&lt;td&gt;ViewModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ScreenEvent&lt;/td&gt;
&lt;td&gt;ViewModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ScreenIntent&lt;/td&gt;
&lt;td&gt;ViewModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ContentState&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ContentIntent&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;元件&lt;/th&gt;
&lt;th&gt;資料流向&lt;/th&gt;
&lt;th&gt;關鍵實作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ViewModel&lt;/td&gt;
&lt;td&gt;發出 &lt;code&gt;ScreenState&lt;/code&gt; / &lt;code&gt;ScreenEvent&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;StateFlow&lt;/code&gt; (持續狀態), &lt;code&gt;SharedFlow&lt;/code&gt; (一次性事件)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen Controller&lt;/td&gt;
&lt;td&gt;向下傳遞 &lt;code&gt;ContentState&lt;/code&gt;&lt;br&gt;向上傳遞&lt;code&gt;ScreenIntent&lt;/code&gt;&lt;br&gt;接收 &lt;code&gt;ScreenEvent&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LaunchedEffect(viewModel.event)&lt;/code&gt;&lt;br&gt;&lt;code&gt;viewModel.onIntent(ClickDetails)&lt;/code&gt; &lt;br&gt;&lt;code&gt;viewModel.state.collectAsStateWithLifecycle()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;接收 &lt;code&gt;ContentState&lt;/code&gt;&lt;br&gt;發出 &lt;code&gt;ContentIntent&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Button(onClick = { ... send Intent })&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
    </item>
    <item>
      <title>[太長;不看... EP1] Android App Architecture 基本盤</title>
      <dc:creator>Lettucech</dc:creator>
      <pubDate>Mon, 21 Jul 2025 18:55:19 +0000</pubDate>
      <link>https://dev.to/lettucech/tai-chang-bu-kan-ep1-android-app-architecture-ji-ben-pan-46ah</link>
      <guid>https://dev.to/lettucech/tai-chang-bu-kan-ep1-android-app-architecture-ji-ben-pan-46ah</guid>
      <description>&lt;h1&gt;
  
  
  到底在說什麼？
&lt;/h1&gt;

&lt;p&gt;Android App Architecture，俗一點就是把各file分好職責，安放在合理的package內。即使個人project也建議應用，對project管理絕對有利。這裡已預設大家會使用MVVM或MVI pattern，如果不是的話...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qkvtgnlrlr9cu3nvcmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qkvtgnlrlr9cu3nvcmk.png" alt="meme" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  為什麼需要？
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;職責清晰。界面不對找UI，資料不對找repository。&lt;/li&gt;
&lt;li&gt;有利多人同時開發&lt;/li&gt;
&lt;li&gt;有利testing/mocking。&lt;del&gt;API還未上線也能開發。🥲&lt;/del&gt;
&lt;/li&gt;
&lt;li&gt;跟modularization相輔相乘&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Project裡有什麼？
&lt;/h1&gt;

&lt;h2&gt;
  
  
  UI Layer
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名稱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;泛指&lt;code&gt;View&lt;/code&gt;/&lt;code&gt;Fragment&lt;/code&gt;/&lt;code&gt;Activity&lt;/code&gt;/&lt;code&gt;Composable&lt;/code&gt;等等。在Android世界，也有部份充當controller的角色。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View Model&lt;/td&gt;
&lt;td&gt;管理state，執行business logic，從repository等等存取UI需要的資料。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Domain Layer
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名稱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;定義物件。如果App自己沒有很強的獨立世界觀，一般會跟services共用。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use Case&lt;/td&gt;
&lt;td&gt;提供封裝好的business logic，供不同的view model重用。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository&lt;/td&gt;
&lt;td&gt;根據business需要，從service等存取資料。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Data Layer
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名稱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;觸發API，存取database/cache等等。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  具體的例子呢？
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── MainActivity (coordinate all layers to make app works)
├── ui/
│   ├── CheckoutScreen
│   └── CheckoutViewModel
├── domain/
│   ├── Cart (model)
│   ├── CartRepository (interface)
│   └── CalculateTotalAmountUseCase
└── data/
    ├── RemoteCartRepository (implementation)
    └── OrderService
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  必需遵守clean architecture的原則，否則破功
&lt;/h1&gt;

&lt;p&gt;允許的關係只有:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MainActivity&lt;/code&gt; -&amp;gt; &lt;code&gt;ui/*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MainActivity&lt;/code&gt; -&amp;gt; &lt;code&gt;domain/*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MainActivity&lt;/code&gt; -&amp;gt; &lt;code&gt;data/*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ui/*&lt;/code&gt; -&amp;gt; &lt;code&gt;domain/*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data/*&lt;/code&gt; -&amp;gt; &lt;code&gt;domain/*&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  結尾
&lt;/h1&gt;

&lt;p&gt;現在，參照提供的例子，試想像一下當中每一個module是如何跟其他module溝通。它們的角色又是負責了什麼？看看自己有沒有真正理解好architecture!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
